javascript Angular $ watch,$ watchCollection,$ attrs。$ observe

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了javascript Angular $ watch,$ watchCollection,$ attrs。$ observe相关的知识,希望对你有一定的参考价值。

//
// ANGULAR ELEMENT
//
// Ampliar: https://docs.angularjs.org/api/ng/function/angular.element
// angular.element acts as the interface between jQuery/jqLite and AngularJS.
// * note that all 'element' references in Angular are always wrapped with jQuery or jqLite; they are never raw DOM references
// ** NEVER USE ANGULAR ELEMENT IN CONTROLLERS (THEY ARE FOR BUSINESS LOGIC) ONLY MANIPULATE DOM IN DIRECTIVES!!!! **

<dumb-password></dumb-password>
//

// dumbpass.html
<div>
  <input type="text" ng-model="model.input">
</div>

//

var app = angular.module("app", [])

app.directive("dumbPassword", function () {

  // Create a DOM element via angular.element	
  var validElement = angular.element('<div>{{ model.input }}</div>'); // Use angular.element to create an element from the string:

  // Link function defined outside as a variable
  var link = function (scope) {
    scope.$watch("model.input", function (value) { //Invoking scope.$watch on model.input sets a listener on that model and waits for it to change. When it changes, the value parameter can be used to check the new value in the listener function.
      if(value === "password") {
        validElement.toggleClass("alert-box alert"); //referencing our previously defined element is cleaner
      }
    });
  };

  return {
    restrict: "E", //replace the <dumb-password> element
    replace: true,
    templateUrl: "dumbpass.html", //inject dumbpass.html template
    compile: function (tElem) { // The compile service takes an HTML string and compiles it into a template function, returning a 'link' function which is used to bind template to a scope.
    // Calling this linking function will then return the element of the template.
      tElem.append(validElement); //tElem parameter is the jqLite wrapper of the dumbpass.html template. Since we created the angular.element reference before as a variable in the directive, the tElem jqLite API is used to append the element validElement.
      return link; //The $compile service returns a link function, that we've moved into a variable, which is a bit cleaner
    }
  }
});


//
// TRACK CHANGES IN PROPERTIES/MODEL: $scope.$watch / $watchCollection / $attrs.$observe
//

// observe changes in a particular object inside a model -typically from inside a controller or a 'link' function inside a directive

//CounterController.js
function CounterController($scope){
  this.count = 0;
  this.increment = function(){
    this.count++;
  };
  this.decrement = function(){
    this.count--;
  };

  // track the changes in this.count: $watch is a special method that comes with the $scope object, so we need to keep it injected inside our controller
  $scope.$watch(angular.bind(this, function(){ // First argument is a string or a function (here, the 'count' model).
    return this.count; //'this' here is referencing $scope, which is the object calling the function, so we bind the 'this' in a lexical context back to the function (via angular's own .bind)
  }), function(newValue, oldValue){ // Second argument is a function, parameters: newValue of the property, oldValue prior to the change
  	// $watch function runs at runtime (needs to set this initial changed values).
	// So you can check initial values for running your logic conditionally on init:
  	if(newValue === oldValue){
      return;
    }
    console.log(newValue, oldValue);
  });
}

// * IMPORTANT TO REMEMBER
// - use $scope for only events and things like $scope.watch and $watchCollection

// ** FOR DEEPER OBJECTS OR ARRAYS, USE $watchCollection
// We're going to create countList where we're going to store all the changes happened to our counter:

function CounterController($scope){
  this.count = 0;
  this.countList = []; // create countList where we're going to store all the changes happened to our counter
  this.increment = function(){
    this.count++;
	this.countList.unshift = {id: this.count}; // push change to the countList
  };
  this.decrement = function(){
    this.count--;
	this.countList.unshift = {id: this.count};
  };
  ...
}

// You can watch arrays or objects using $watch:
$scope.$watch(angular.bind(this, function(){
	return this.countList; 
}), function(newValue, oldValue){ 
	console.log(newValue, oldValue);
}, true); //you need this third argument, 'true', to do a deep watch in this particular property we want to observe

// But it's more performant to use $watchCollection, to track changes in arrays and objects in a more performant way, without using deep watch:
$scope.$watchCollection(angular.bind(this, function(){
	return this.countList; 
}), function(newValue, oldValue){ 
	console.log(newValue, oldValue);
});

// ** IF WE WANT TO TRACK CHANGES IN ATTRIBUTES WHEN USING DIRECTIVES, USE $attrs.$observe
// $attrs.$observe doesn't actually set a watcher, so it's more performant than $scope.$watch
// * $attrs.$observe only works in interpolated values that are dynamically driven through angular

<counter name="{{ counter.name }}"></counter>
//

function counter(){
  return {
    template: `
      <div class="counter">
        <input type="text" ng-model="counter.count">
        <button type="button ng-click="counter.decrement();"> - </button>
        <button type="button ng-click="counter.increment();"> + </button>
      </div>
    `,
    link: function($scope, $element, $attrs) {
      $attrs.$observe('name', function(value){ //we want to observe the 'name' attribute. We pass the interpolated value to the function
	    if(value === 'Food counter'){
          $attrs.$updateClass('counter--food', 'counter--drink'); //second parameter is the otherwise value, if the condition doesn't match -- updateClass es like: remove all classes and add this class?
        } else if(value === 'Food counter'){
          $attrs.$updateClass('counter--drink', 'counter--food');
        }
      });
    }
  };
}

以上是关于javascript Angular $ watch,$ watchCollection,$ attrs。$ observe的主要内容,如果未能解决你的问题,请参考以下文章

Angular2 无法使用 javascript 导入 FormsModule

如何为现有的 Javascript 库创建 Angular 库包装器?

从javascript 调用angular的函数

另一个 javascript 库上的 Angular 2 templateURL 功能

从 Angular 组件动态加载外部 javascript 文件

将外部css和javascript文件导入Angular 4