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

Fix ngClass mixed array/object when object instance does not changes #14405

Closed
wants to merge 3 commits into from

Conversation

drpicox
Copy link
Contributor

@drpicox drpicox commented Apr 9, 2016

What kind of change does this PR introduce? (Bug fix, feature, docs update, ...) bugfix

What is the current behavior? (You can also link to an open issue here): when the expression is [{}] it does not tracks changes inside the object

  it('should support changing multiple classes via variable array mixed with conditionally via a map', inject(function($rootScope, $compile) {
    $rootScope.classVar = ['', {orange: true}];
    element = $compile('<div class="existing" ng-class="classVar"></div>')($rootScope);
    $rootScope.$digest();

    $rootScope.classVar[1].orange = false;
    $rootScope.$digest();

    expect(element.hasClass('orange')).toBeFalsy(); /// <<--- this fails before this patch
  }));

What is the new behavior (if this is a feature change)?: now it tracks changes correctly

Does this PR introduce a breaking change?: nop

Please check if the PR fulfills these requirements

Other information:

In this plnkr you can see the failure:

#14404 also fixes this problem by doing a refactor. (it tries to be a performance optimization)
This PR can be combined into #14394 if you ask.

@drpicox drpicox changed the title Ng class fix mixed var Fix ngClass mixed array/object when object instance does not changes Apr 9, 2016
@drpicox
Copy link
Contributor Author

drpicox commented Apr 10, 2016

As I commented in #14394 there is this case that are not resolved here and it has two solutions. The problem:

  // this test actually passes
  it('should support mixed nested array and object leafs variable', inject(function($rootScope, $compile) {
    $rootScope.classVar = ['red', ['green', {blue: true}]];
    element = $compile('<div class="existing" ng-class="classVar"></div>')($rootScope);
    $rootScope.$digest();

    expect(element.hasClass('red')).toBeTruthy();
    expect(element.hasClass('green')).toBeTruthy();
    expect(element.hasClass('blue')).toBeTruthy();
  }));

  // this test fails even with this patch
  it('should support mixed nested array and object leafs variable with mutating object', inject(function($rootScope, $compile) {
    $rootScope.classVar = ['red', ['green', {blue: true}]];
    element = $compile('<div class="existing" ng-class="classVar"></div>')($rootScope);
    $rootScope.$digest();

    $rootScope.classVar[1][1].blue = false;

    expect(element.hasClass('red')).toBeTruthy();
    expect(element.hasClass('green')).toBeTruthy();
    expect(element.hasClass('blue')).toBeFalsy();
  }));

Solutions:

  1. Do not accept nested array syntax: it would be "breaking changes" so any current implementation that it is using it. But it is unlikely because it is a not documented behaviour and the tracking of changes is not working properly. It have to be checked what happens in Angular2, because Angular2 uses this concept of nested array to express Injection and Component dependencies it is possible that feels comfortable for the programmer having nested arrays.
  2. Deep solve change tracking only for arrays: it will not break the implementation and only will cost nothing (except when using nested arrays because the checking would be done properly). In such case the implementation of perf(ngClass): refactor to optimize the case of static map of classes with large objects #14404 is already solving this.

¿Which do you consider more acceptable?

@gkalpak
Copy link
Member

gkalpak commented Apr 10, 2016

The docs are very explicit about what types of expressions are supported in ngClass. Nested arrays are not supported, so if anyone uses them, they are out of luck imho.

According to the docs, the expression used with ngClass should evaluate to one of 3 things:

  1. A string.
  2. An object.
  3. An array containing strings and/or objects.

(Where objects are treated as key-value pairs, with keys denoting classes and values determining the presense or absense of the corresponding key/class.)

@@ -409,6 +409,18 @@ describe('ngClass', function() {
expect(e2.hasClass('even')).toBeTruthy();
expect(e2.hasClass('odd')).toBeFalsy();
}));

it('should support changing multiple classes via variable array mixed with conditionally via a map', inject(function($rootScope, $compile) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pleasd wrap lines at 100 chars.

@gkalpak
Copy link
Member

gkalpak commented Apr 10, 2016

I left a couple of minor comments wrt the test, but LGTM otherwise. 👍
Feel free to ping me once you address them.

@drpicox drpicox force-pushed the ngClass-fix-mixedVar branch from 727ab82 to 3b91bb1 Compare April 10, 2016 16:43
@drpicox
Copy link
Contributor Author

drpicox commented Apr 10, 2016

@gkalpak I think that is now right.
Just ask and I'll squash all commits in one.

@gkalpak
Copy link
Member

gkalpak commented Apr 11, 2016

It's OK, I'll squash while merging. Thx 👍

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

Successfully merging this pull request may close these issues.

3 participants