Repository: vuejs/vue
Pull request: :class bindings with array + objects

For feature request: #2530 

Feel free to suggest any improvements or a better way of going about the proposed solution.


Changed files:
filename: src/directives/internal/class.js
patch: @@ -2,8 +2,7 @@ import {
   addClass,
   removeClass,
   isArray,
-  isPlainObject,
-  hasOwn
+  isPlainObject
 } from '../../util/index'
 
 export default {
@@ -24,22 +23,18 @@ export default {
 
   handleObject (value) {
     this.cleanup(value)
-    var keys = this.prevKeys = Object.keys(value)
-    for (var i = 0, l = keys.length; i < l; i++) {
-      var key = keys[i]
-      if (value[key]) {
-        addClass(this.el, key)
-      } else {
-        removeClass(this.el, key)
-      }
-    }
+    this.prevKeys = Object.keys(value)
+    setObjectClasses(this.el, value)
   },
 
   handleArray (value) {
     this.cleanup(value)
     for (var i = 0, l = value.length; i < l; i++) {
-      if (value[i]) {
-        addClass(this.el, value[i])
+      var val = value[i]
+      if (val && isPlainObject(val)) {
+        setObjectClasses(this.el, val)
+      } else if (val && typeof val === 'string') {
+        addClass(this.el, val)
       }
     }
     this.prevKeys = value.slice()
@@ -50,14 +45,30 @@ export default {
       var i = this.prevKeys.length
       while (i--) {
         var key = this.prevKeys[i]
-        if (key && (!value || !contains(value, key))) {
+        if (!key) continue
+        if (isPlainObject(key)) {
+          var keys = Object.keys(key)
+          for (var k = 0; k < keys.length; k++) {
+            removeClass(this.el, keys[k])
+          }
+        } else {
           removeClass(this.el, key)
         }
       }
     }
   }
 }
 
+function setObjectClasses (el, obj) {
+  var keys = Object.keys(obj)
+  for (var i = 0, l = keys.length; i < l; i++) {
+    var key = keys[i]
+    if (obj[key]) {
+      addClass(el, key)
+    }
+  }
+}
+
 function stringToObject (value) {
   var res = {}
   var keys = value.trim().split(/\s+/)
@@ -67,9 +78,3 @@ function stringToObject (value) {
   }
   return res
 }
-
-function contains (value, key) {
-  return isArray(value)
-    ? value.indexOf(key) > -1
-    : hasOwn(value, key)
-}

filename: test/unit/specs/directives/internal/class_spec.js
patch: @@ -42,7 +42,7 @@ describe(':class', function () {
     dir.update(['b', 'c'])
     expect(el.className).toBe('a b c')
     dir.update(['d', 'c'])
-    expect(el.className).toBe('a c d')
+    expect(el.className).toBe('a d c')
     dir.update()
     expect(el.className).toBe('a')
     // test mutating array
@@ -53,5 +53,18 @@ describe(':class', function () {
     arr.push('f')
     dir.update(arr)
     expect(el.className).toBe('a f')
+    // test array with objects
+    dir.update(['x', {y: true, z: true}])
+    expect(el.className).toBe('a x y z')
+    dir.update(['x', {y: true, z: false}])
+    expect(el.className).toBe('a x y')
+    dir.update(['f', {z: true}])
+    expect(el.className).toBe('a f z')
+    dir.update(['l', 'f', {n: true, z: true}])
+    expect(el.className).toBe('a l f n z')
+    dir.update(['x', {}])
+    expect(el.className).toBe('a x')
+    dir.update()
+    expect(el.className).toBe('a')
   })
 })