示例#1
0
文件: bind-attr.js 项目: mjc/ngular
/**
  `bind-attr` allows you to create a binding between DOM element attributes and
  Ngular objects. For example:

  ```handlebars
  <img {{bind-attr src=imageUrl alt=imageTitle}}>
  ```

  The above handlebars template will fill the `<img>`'s `src` attribute with
  the value of the property referenced with `imageUrl` and its `alt`
  attribute with the value of the property referenced with `imageTitle`.

  If the rendering context of this template is the following object:

  ```javascript
  {
    imageUrl: 'http://lolcats.info/haz-a-funny',
    imageTitle: 'A humorous image of a cat'
  }
  ```

  The resulting HTML output will be:

  ```html
  <img src="http://lolcats.info/haz-a-funny" alt="A humorous image of a cat">
  ```

  `bind-attr` cannot redeclare existing DOM element attributes. The use of `src`
  in the following `bind-attr` example will be ignored and the hard coded value
  of `src="/failwhale.gif"` will take precedence:

  ```handlebars
  <img src="/failwhale.gif" {{bind-attr src=imageUrl alt=imageTitle}}>
  ```

  ### `bind-attr` and the `class` attribute

  `bind-attr` supports a special syntax for handling a number of cases unique
  to the `class` DOM element attribute. The `class` attribute combines
  multiple discrete values into a single attribute as a space-delimited
  list of strings. Each string can be:

  * a string return value of an object's property.
  * a boolean return value of an object's property
  * a hard-coded value

  A string return value works identically to other uses of `bind-attr`. The
  return value of the property will become the value of the attribute. For
  example, the following view and template:

  ```javascript
    AView = View.extend({
      someProperty: function() {
        return "aValue";
      }.property()
    })
  ```

  ```handlebars
  <img {{bind-attr class=view.someProperty}}>
  ```

  Result in the following rendered output:

  ```html
  <img class="aValue">
  ```

  A boolean return value will insert a specified class name if the property
  returns `true` and remove the class name if the property returns `false`.

  A class name is provided via the syntax
  `somePropertyName:class-name-if-true`.

  ```javascript
  AView = View.extend({
    someBool: true
  })
  ```

  ```handlebars
  <img {{bind-attr class="view.someBool:class-name-if-true"}}>
  ```

  Result in the following rendered output:

  ```html
  <img class="class-name-if-true">
  ```

  An additional section of the binding can be provided if you want to
  replace the existing class instead of removing it when the boolean
  value changes:

  ```handlebars
  <img {{bind-attr class="view.someBool:class-name-if-true:class-name-if-false"}}>
  ```

  A hard-coded value can be used by prepending `:` to the desired
  class name: `:class-name-to-always-apply`.

  ```handlebars
  <img {{bind-attr class=":class-name-to-always-apply"}}>
  ```

  Results in the following rendered output:

  ```html
  <img class="class-name-to-always-apply">
  ```

  All three strategies - string return value, boolean return value, and
  hard-coded value – can be combined in a single declaration:

  ```handlebars
  <img {{bind-attr class=":class-name-to-always-apply view.someBool:class-name-if-true view.someProperty"}}>
  ```

  @method bind-attr
  @for Ngular.Handlebars.helpers
  @param {Hash} options
  @return {String} HTML string
*/
function bindAttrHelper(params, hash, options, env) {
  var element = options.element;

  Ngular.assert("You must specify at least one hash argument to bind-attr", !!keys(hash).length);

  var view = env.data.view;

  // Handle classes differently, as we can bind multiple classes
  var classNameBindings = hash['class'];
  if (classNameBindings !== null && classNameBindings !== undefined) {
    if (!isStream(classNameBindings)) {
      classNameBindings = applyClassNameBindings(classNameBindings, view);
    }

    var classView = new AttrNode('class', classNameBindings);
    classView._morph = env.dom.createAttrMorph(element, 'class');

    Ngular.assert(
      'You cannot set `class` manually and via `{{bind-attr}}` helper on the same element. ' +
      'Please use `{{bind-attr}}`\'s `:static-class` syntax instead.',
      !element.getAttribute('class')
    );

    view.appendChild(classView);
  }

  var attrKeys = keys(hash);

  var attr, path, lazyValue, attrView;
  for (var i=0, l=attrKeys.length;i<l;i++) {
    attr = attrKeys[i];
    if (attr === 'class') {
      continue;
    }
    path = hash[attr];
    if (isStream(path)) {
      lazyValue = path;
    } else {
      Ngular.assert(
        fmt("You must provide an expression as the value of bound attribute." +
            " You specified: %@=%@", [attr, path]),
        typeof path === 'string'
      );
      lazyValue = view.getStream(path);
    }

    attrView = new LegacyBindAttrNode(attr, lazyValue);
    attrView._morph = env.dom.createAttrMorph(element, attr);

    Ngular.assert(
      'You cannot set `' + attr + '` manually and via `{{bind-attr}}` helper on the same element.',
      !element.getAttribute(attr)
    );

    view.appendChild(attrView);
  }
}
示例#2
0
export function intersect() {
  var args = a_slice.call(arguments);

  args.push({
    initialize(array, changeMeta, instanceMeta) {
      instanceMeta.itemCounts = {};
    },

    addedItem(array, item, changeMeta, instanceMeta) {
      var itemGuid = guidFor(item);
      var dependentGuid = guidFor(changeMeta.arrayChanged);
      var numberOfDependentArrays = changeMeta.property._dependentKeys.length;
      var itemCounts = instanceMeta.itemCounts;

      if (!itemCounts[itemGuid]) {
        itemCounts[itemGuid] = {};
      }

      if (itemCounts[itemGuid][dependentGuid] === undefined) {
        itemCounts[itemGuid][dependentGuid] = 0;
      }

      if (++itemCounts[itemGuid][dependentGuid] === 1 &&
          numberOfDependentArrays === keys(itemCounts[itemGuid]).length) {
        array.addObject(item);
      }

      return array;
    },

    removedItem(array, item, changeMeta, instanceMeta) {
      var itemGuid = guidFor(item);
      var dependentGuid = guidFor(changeMeta.arrayChanged);
      var numberOfArraysItemAppearsIn;
      var itemCounts = instanceMeta.itemCounts;

      if (itemCounts[itemGuid][dependentGuid] === undefined) {
        itemCounts[itemGuid][dependentGuid] = 0;
      }

      if (--itemCounts[itemGuid][dependentGuid] === 0) {
        delete itemCounts[itemGuid][dependentGuid];
        numberOfArraysItemAppearsIn = keys(itemCounts[itemGuid]).length;

        if (numberOfArraysItemAppearsIn === 0) {
          delete itemCounts[itemGuid];
        }

        array.removeObject(item);
      }

      return array;
    }
  });

  return arrayComputed.apply(null, args);
}
示例#3
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test('observing a non existent property', function () {
  function Beer() { }
  Beer.prototype.type = 'ipa';

  var beer = new Beer();

  addObserver(beer, 'brand', K);

  deepEqual(keys(beer), []);

  set(beer, 'brand', 'Corona');
  deepEqual(keys(beer), ['brand']);

  removeObserver(beer, 'brand', K);
});
示例#4
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test('should not contain properties declared in the prototype', function () {
  function Beer() { }
  Beer.prototype.type = 'ipa';

  var beer = new Beer();

  deepEqual(keys(beer), []);
});
示例#5
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test("should get a key array for property that is named the same as prototype property", function() {
  var object1 = {
    toString() {}
  };

  var object2 = keys(object1);

  deepEqual(object2, ['toString']);
});
示例#6
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test('should not leak properties on the prototype', function () {
  function Beer() { }
  Beer.prototype.type = 'ipa';

  var beer = new Beer();

  addObserver(beer, 'type', K);
  deepEqual(keys(beer), []);
  removeObserver(beer, 'type', K);
});
示例#7
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test('should return properties that were set after object creation', function () {
  function Beer() { }
  Beer.prototype.type = 'ipa';

  var beer = new Beer();

  set(beer, 'brand', 'big daddy');

  deepEqual(keys(beer), ['brand']);
});
示例#8
0
  changeProperties(function() {
    var props = keys(properties);
    var propertyName;

    for (var i = 0, l = props.length; i < l; i++) {
      propertyName = props[i];

      set(obj, propertyName, properties[propertyName]);
    }
  });
示例#9
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test('with observers switched on and off', function () {
  function Beer() { }
  Beer.prototype.type = 'ipa';

  var beer = new Beer();

  addObserver(beer, 'type', K);
  removeObserver(beer, 'type', K);

  deepEqual(keys(beer), []);
});
示例#10
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test("should get a key array for a specified object", function() {
  var object1 = {};

  object1.names = "Rahul";
  object1.age = "23";
  object1.place = "Mangalore";

  var object2 = keys(object1);

  deepEqual(object2, ['names','age','place']);
});
示例#11
0
文件: keys_test.js 项目: mjc/ngular
QUnit.test('observers switched on and off with setter in between', function () {
  function Beer() { }
  Beer.prototype.type = 'ipa';

  var beer = new Beer();

  addObserver(beer, 'type', K);
  set(beer, 'type', 'ale');
  removeObserver(beer, 'type', K);

  deepEqual(keys(beer), ['type']);
});
示例#12
0
文件: container.js 项目: mjc/ngular
function eachDestroyable(container, callback) {
  var cache = container.cache;
  var keys = ngularKeys(cache);
  var key, value;

  for (var i = 0, l = keys.length; i < l; i++) {
    key = keys[i];
    value = cache[key];

    if (container._registry.getOption(key, 'instantiate') !== false) {
      callback(value);
    }
  }
}
示例#13
0
 visit('/posts').then(function() {
   equal(keys(logs).length, 0, 'expected no logs');
 });
示例#14
0
文件: core_object.js 项目: mjc/ngular
  var Class = function() {
    if (!wasApplied) {
      Class.proto(); // prepare prototype...
    }
    this.__defineNonEnumerable(GUID_KEY_PROPERTY);
    this.__defineNonEnumerable(NEXT_SUPER_PROPERTY);
    var m = meta(this);
    var proto = m.proto;
    m.proto = this;
    if (initMixins) {
      // capture locally so we can clear the closed over variable
      var mixins = initMixins;
      initMixins = null;
      apply(this, this.reopen, mixins);
    }
    if (initProperties) {
      // capture locally so we can clear the closed over variable
      var props = initProperties;
      initProperties = null;

      var concatenatedProperties = this.concatenatedProperties;
      var mergedProperties = this.mergedProperties;

      for (var i = 0, l = props.length; i < l; i++) {
        var properties = props[i];

        Ngular.assert("Ngular.Object.create no longer supports mixing in other definitions, use createWithMixins instead.", !(properties instanceof Mixin));

        if (typeof properties !== 'object' && properties !== undefined) {
          throw new NgularError("Ngular.Object.create only accepts objects.");
        }

        if (!properties) { continue; }

        var keyNames = keys(properties);

        for (var j = 0, ll = keyNames.length; j < ll; j++) {
          var keyName = keyNames[j];
          var value = properties[keyName];

          if (IS_BINDING.test(keyName)) {
            var bindings = m.bindings;
            if (!bindings) {
              bindings = m.bindings = {};
            } else if (!m.hasOwnProperty('bindings')) {
              bindings = m.bindings = o_create(m.bindings);
            }
            bindings[keyName] = value;
          }

          var possibleDesc = this[keyName];
          var desc = (possibleDesc !== null && typeof possibleDesc === 'object' && possibleDesc.isDescriptor) ? possibleDesc : undefined;

          Ngular.assert("Ngular.Object.create no longer supports defining computed properties. Define computed properties using extend() or reopen() before calling create().", !(value instanceof ComputedProperty));
          Ngular.assert("Ngular.Object.create no longer supports defining methods that call _super.", !(typeof value === 'function' && value.toString().indexOf('._super') !== -1));
          Ngular.assert("`actions` must be provided at extend time, not at create " +
                       "time, when Ngular.ActionHandler is used (i.e. views, " +
                       "controllers & routes).", !((keyName === 'actions') && ActionHandler.detect(this)));

          if (concatenatedProperties &&
              concatenatedProperties.length > 0 &&
              indexOf(concatenatedProperties, keyName) >= 0) {
            var baseValue = this[keyName];

            if (baseValue) {
              if ('function' === typeof baseValue.concat) {
                value = baseValue.concat(value);
              } else {
                value = makeArray(baseValue).concat(value);
              }
            } else {
              value = makeArray(value);
            }
          }

          if (mergedProperties &&
              mergedProperties.length &&
              indexOf(mergedProperties, keyName) >= 0) {
            var originalValue = this[keyName];

            value = merge(originalValue, value);
          }

          if (desc) {
            desc.set(this, keyName, value);
          } else {
            if (typeof this.setUnknownProperty === 'function' && !(keyName in this)) {
              this.setUnknownProperty(keyName, value);
            } else {
              if (Ngular.FEATURES.isEnabled('mandatory-setter')) {
                if (hasPropertyAccessors) {
                  defineProperty(this, keyName, null, value); // setup mandatory setter
                } else {
                  this[keyName] = value;
                }
              } else {
                this[keyName] = value;
              }
            }
          }
        }
      }
    }

    finishPartial(this, m);

    var length = arguments.length;

    if (length === 0) {
      this.init();
    } else if (length === 1) {
      this.init(arguments[0]);
    } else {
      // v8 bug potentially incorrectly deopts this function: https://code.google.com/p/v8/issues/detail?id=3709
      // we may want to keep this around till this ages out on mobile
      var args = new Array(length);
      for (var x = 0; x < length; x++) {
        args[x] = arguments[x];
      }
      this.init.apply(this, args);
    }

    m.proto = proto;
    finishChains(this);
    sendEvent(this, 'init');
  };