Skip to content
This repository has been archived by the owner on Apr 12, 2024. It is now read-only.

ngIf with replace:true on directive template #9837

Open
bertrandgressier opened this issue Oct 30, 2014 · 32 comments
Open

ngIf with replace:true on directive template #9837

bertrandgressier opened this issue Oct 30, 2014 · 32 comments

Comments

@bertrandgressier
Copy link

Hello team,

We have a regression with the last angular version (since v1.3.0-beta.11)

In this commit d71df9f#diff-1c8fbee1402a2ef3c203def41d1960cdR97, you modified the child scope ngIf uses. Now with this modification, using ngIf on the root element of a directive template that is configured with replace:true, the $scope is not the expected one. (it's not the same)

We tried to build a reduced test case to show the regression :

As you can see, the first directive failed in beta 11.

What do you think ? Is it a bug or the new expected behaviour ?
If so, maybe we should detail the documentation.

thx!

@clakech
Copy link
Contributor

clakech commented Oct 30, 2014

Hi, @caitp could you help us on this one? ;-)

@caitp
Copy link
Contributor

caitp commented Oct 30, 2014

since you asked nicely ^^

@caitp
Copy link
Contributor

caitp commented Oct 30, 2014

so, you can work around this by not using an element transclusion directive as the root element of the template --- I'm not sure exactly why this worked in beta 10 though, doesn't make a lot of sense.

Lets dig through the changelog and see if it's listed in any of the breaking changes

@caitp
Copy link
Contributor

caitp commented Oct 30, 2014

@petebacondarwin there are no breaking changes for $compile in beta 11, i think we forgot to add them

@caitp
Copy link
Contributor

caitp commented Oct 30, 2014

I wonder if we could make this more palatable --- like, if you have element transclusion at the root of a replacement template, it should add the original parent scope in a custom data field, and use that when getting the parent scope.

@clakech
Copy link
Contributor

clakech commented Oct 30, 2014

Thank you for your help about this! I knew your advice would be useful! ^^

We would definitely love to see a breaking change in the changelog and a new palatable behavior of angular in this use case.

We hesitate between removing replace:true and avoid using element transclusion directive as root element.

@clakech
Copy link
Contributor

clakech commented Oct 30, 2014

Ok so I added another exemple in the jsbin with a use case where the ng-if is not on the root element of the directive:

http://jsbin.com/fahoz/1/edit?html,js,output

@pocesar
Copy link
Contributor

pocesar commented Nov 7, 2014

seeing this as well, since upgrading to 1.3.1. my template is

template =  '<div class="tab-content" ng-if="visible">' +
                    '<div ng-if="tab" class="tab-inner-content clearfix" ng-include="tab"></div>' +
                   '</div>';

and it broke

@clakech
Copy link
Contributor

clakech commented Nov 7, 2014

OK so someone need to propose a breaking change for this but where should we document this ? in ngIf doc ? in directive doc ? in changelog ?

@petebacondarwin petebacondarwin added this to the 1.3.x milestone Nov 10, 2014
@petebacondarwin petebacondarwin modified the milestones: 1.3.3, 1.3.x Nov 10, 2014
@petebacondarwin petebacondarwin self-assigned this Nov 10, 2014
@petebacondarwin
Copy link
Contributor

Only just seen this one. Let me take a look this week.

@marlun78
Copy link

I had this issue too. It seems to work if you wrap the root element (with the ngIf) in a new element (without ngIf) - as a temporary fix.
http://plnkr.co/edit/9wgADGTILYINqHEdY1KE?p=preview

@petebacondarwin
Copy link
Contributor

Moving to 1.3.4 as I suspect that this is going to be a tricky fix (and not something that can be knocked up and suitably reviewed by Monday).

@lgalfaso lgalfaso removed this from the 1.4.0-beta.2 / 1.3.11 milestone Feb 5, 2015
@matias-sandell
Copy link

Hi, has there been any progress on this, @petebacondarwin ?

@petebacondarwin
Copy link
Contributor

Directives that do replace cause so many problems. Given that we can almost always work around it with non-replace directives, I am going to move this to the Ice Box for now.

@petebacondarwin petebacondarwin modified the milestones: Ice Box, 1.4.x Jul 30, 2015
@petebacondarwin petebacondarwin removed their assignment Jul 30, 2015
@thetrevdev
Copy link

Is there any way to detect this and throw a warning?

I understand all the reasons to not fix and to deprecate replace... But as far as migrating an existing project this is a breaking change that isn't documented.
https://code.angularjs.org/1.3.18/docs/guide/migration#angular-html-compiler-compile-
This refers to other more broken use cases I feel.

We are migrating large apps that happened to have used replace: true and its difficult to know where we might have used transclusion. If we could at least identify or warn we could go and fix the replace attribute. Right now trying to replace every single directive across many apps would be quite difficult.

@petebacondarwin
Copy link
Contributor

@thetrevdev - as a start you could do a search of your code base for directives that employ replace: true. Then look at their templates. If there is one of ng-if, ng-repeat or ng-switch on the root node of the template then you will need to fix it.

@thetrevdev
Copy link

Thats pretty difficult to do with a very large app.
I have currently made a local fork of angular that errors when it detects this case while migrating.
When templates are involved and have isolate scope it marks the directives. I check here for the offending transclusion.

https://github.com/angular/angular.js/blob/master/src/ng/compile.js#L2104

@petebacondarwin
Copy link
Contributor

@thetrevdev - glad you have got something working for you. If it turns out to be very helpful perhaps you could submit a patch?

@petebacondarwin
Copy link
Contributor

I think that this may be fixed by #12975

@JaKXz
Copy link

JaKXz commented Jan 25, 2016

I think I'm still seeing this issue on v1.4.8. My directive had replace: true and an ng-if on the root element. This let the directive be rendered but none of the children elements had the correct scope. Removing replace: true fixed the issue.

@adamreisnz
Copy link

Also still experiencing this issue. I will look at avoiding replace: true, but it's going to be a bit of a pain.

@petebacondarwin
Copy link
Contributor

@adambuczynski - other than CSS that is too constraining, are there any other reason why this is a pain?

@adamreisnz
Copy link

@petebacondarwin Yeah, CSS was one of the initial pains I ran into, but it was late at night and today's a new day, so it might turn out not as bad as I originally thought! :)

There are a few other reasons I identified yesterday thought, one is that we need to apply an ng-if condition on the parent element of the directive. Currently, we are using ng-show for that, but the drawback of that is, that the scope/controller is not re-initialized when the directive is shown. So for that reason we wanted to move to using ng-if (which led me to this issue in the first place).

In order to not have to put the ng-if logic in our main templates, we embed it in the directive itself, which judging from the comments in this thread, seems to be a common use case. However, if the directive is not replacing the parent element, it will not work as expected, as it will only remove the child element, and not the parent element. Moving the ng-if's to the main template is not an option.

Second, to avoid repetition we also apply some classes to the parent element via the directive (via attributes on the directive's template). These get merged nicely now with any classes that are applied to the directive element in the main template. With replace: false this will no longer work, and we'll be forced to apply the classes programmatically via the link function, which isn't the end of the world, but just another (somewhat redundant) step that we have to take.

Anyway, I only found out that replace: true is deprecated by stumbling upon this issue. Most, if not all of our directives use replace: true, so I will run by all of them today and see if any other issues arise if we set it to false. It would have been good to receive deprecated notices in the console though, to alert developers of the fact they are using a deprecated API.

Will report back after I've had another good look at it today, and see if I can work around them while not using replace: true. If any other pain points come up I will let you know.

@petebacondarwin
Copy link
Contributor

@adambuczynski - be aware that though replace:true is deprecated, we will not remove it from Angular 1.x - it is already gone from Angular 2. What it does mean is that we are not going to spend much time trying to fix the many complex and difficult corner case problems it has.

@adamreisnz
Copy link

Thanks, yes I got that form reading more about it. I do intend to migrate to Angular 2 sooner rather than later, so figuring out now how to best use non-replacing directives would be the right step to take.

To be honest though, I don't think wanting to apply ng-if on the parent element via the directive template qualifies as a corner case. It seems like a fairly common use case.

@petebacondarwin
Copy link
Contributor

It may not be "corner" but for sure it is "difficult". I haven't seen any PRs offering to fix it :-)

@adamreisnz
Copy link

@petebacondarwin Well, most directives I was able to convert painlessly, but there were the couple where some CSS tweaks were necessary. As for the one with the ng-if on it, I've managed to refactor it in a way that the ng-if can still be applied on the inner element, and with some CSS tweaks it seems to be looking and working the same now. So didn't need replace: true after all.

Do Angular 2 components offer a way to apply directives and other attributes on to the parent element via the directive templates? Because that currently seems to be the biggest drawback in Angular 1 directives, when not using replace: true.

@LarryTurtis
Copy link

It took me awhile to figure out I had this issue. I spent a fair amount of time assuming I had some sort of error in my scope configuration. It was only on a whim that I removed ng-if from the root element and discovered this to be the problem. It would be good to have some sort of warning.

@douglasg14b
Copy link

Just spent several hours trying to figure out what was wrong with my code to finally narrow it down to the ng-if. Happy there is a known issue...

Narretz added a commit that referenced this issue Apr 9, 2018
Narretz added a commit that referenced this issue Apr 11, 2018
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.
Projects
None yet
Development

No branches or pull requests