Angularjs:如何将数据从自定义指令传递到编译指令的范围

时间:2017-05-14 23:35:08

标签: angularjs

我正在对编译链接编译中的其他指令的指令进行测试,我的问题是我无法弄清楚如何将数据传递到编译的指令范围,也许它应该通过属性或通过范围,但我不能实现这一目标。

我有指令A,其内容是翻译,内容是另一个指令B,指令A将解析数据并克隆多个B指令,具体取决于数据的长度,对于每个数据,应该有一个B指令,其中包含从迭代中收集的数据,使每个BA指令内成为唯一的指令。

所以这是代码:

HTML:

<body ng-app="app" ng-controller="testController">
  <grid rows="3" collection="blogs">
    <blog></blog>
  </grid>
</body>

使用Javascript:

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

app.controller("testController", function($scope) {
  $scope.blogs = [{
    id: 1,
    title: "Blog #1",
    author: "Random user"
  }, {
    id: 2,
    title: "Blog #2",
    author: "Random user"
  }, {
    id: 3,
    title: "Blog #3",
    author: "Random user"
  }, {
    id: 4,
    title: "Blog #4",
    author: "Random user"
  }, {
    id: 5,
    title: "Blog #5",
    author: "Random user"
  }, {
    id: 6,
    title: "Blog #6",
    author: "Random user"
  }, {
    id: 7,
    title: "Blog #7",
    author: "Random user"
  }, {
    id: 8,
    title: "Blog #8",
    author: "Random user"
  }, {
    id: 9,
    title: "Blog #9",
    author: "Random user"
  }];
});

app.directive("blog", function() {
  return {
    restrict: "E",
    template: blogTemplate,
    replace: true,
    scope: {
      data: "@reference"
    },
    controller: controller
  };

  function controller($scope) {
    $scope.access = function() {
      console.log($scope);
    }
  }
});

app.directive("grid", function($compile) {
  return {
    restrict: "E",
    transclude: true,
    scope: {
      columns: "@rows",
      collection: "=?"
    },
    link: link
  };


  function link(scope, element, attr, ctrl, transclude) {
    var _columnLength;
    var _columnTrack = 0;
    var columns = [];
    _columnLength = parseInt(scope.columns);

    // Create columns from row's attribute
    for (i = 0; i < _columnLength; i++) {
      var column = angular.element(document.createElement("div"));

      column.addClass("grid-column");

      columns.push(column);
    }

    // Fill all columns with the passed collection scope
    fillColumns();

    // Render all columns in the DOM
    renderColumns(element, columns);

    function renderColumns(el, columns) {
      for (let col of columns) {
        el.append(col);
      }
    }

    function fillColumns() {
      var collection = scope.collection;

      // Compile multiple transcluded content for each collection item
      for (let data of collection) {
        transclude(scope.$new(), function(clone, innerScope) {
          // Set this property to test if the compiled directive recieves it
          innerScope.test = _columnTrack;

          $compile(clone)(innerScope);

          // Append the compiled clone into its respective column
          appendColumn(clone);
        });
      }
    }

    function appendColumn(data) {
      columns[_columnTrack].append(data);

      if (_columnTrack < (_columnLength - 1)) {
        _columnTrack++;
      } else {
        _columnTrack = 0;
      }

    }
  }

});

我希望创建的范围是在网格链接编译中。如果需要运行时示例,则此处为codepen注意:codepen具有博客模板的变量,因此您可以看到它的结构

2 个答案:

答案 0 :(得分:1)

您需要在子指令上使用隔离范围,以便传入数据。这可以通过在编译之前添加属性来完成。一个大问题是转换是在for循环中,导致编译的元素被transclude重新编译并且范围丢失。

这应该做一次,这样就可以获得指令(网格)内的模板。然后可以使用该模板克隆具有data属性的副本。

var blogTemplate = `
<div class="blog-preview">
	<div class="blog-thumbnail"></div>
	
	<div class="blog-wrapper">
		<div class="blog-head">
			<h2 class="blog-title">{{data.id}}</h2>
			<p class="blog-author">{{data.author}}r</p>
			<p class="blog-date">2017-03-06</p>
		</div>
		
		<div class="blog-actions">
			
		</div>

		<div class="blog-content">
			<p>Lorem ipsum dolor sit amet, consectetur adipisicing elit.</p>

			<button href="#" class="blog-access" ng-click="access()">access</button>
		</div>
	</div>
</div>
`;

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

app.controller("testController", function($scope) {
  $scope.blogs = [
    {
      id: 1,
      title: "Blog #1",
      author: "Random user"
    },
    {
      id: 2,
      title: "Blog #2",
      author: "Random user"
    },
    {
      id: 3,
      title: "Blog #3",
      author: "Random user"
    },
    {
      id: 4,
      title: "Blog #4",
      author: "Random user"
    },
    {
      id: 5,
      title: "Blog #5",
      author: "Random user"
    },
    {
      id: 6,
      title: "Blog #6",
      author: "Random user"
    },
    {
      id: 7,
      title: "Blog #7",
      author: "Random user"
    },
    {
      id: 8,
      title: "Blog #8",
      author: "Random user"
    },
    {
      id: 9,
      title: "Blog #9",
      author: "Random user"
    }
  ];
});

app.directive("blog", function() {
  return {
    restrict: "E",
    template: blogTemplate,
    replace: false,
    scope: {
      data: "=data"
    },
    link: link
  };

  function link($scope) {
    $scope.access = function() {
      console.log($scope.data);
    };
  }
});

app.directive("grid", function($compile) {
  return {
    restrict: "E",
    transclude: true,
    scope: {
      columns: "@rows",
      collection: "=?"
    },
    link: link
  };

  function link(scope, element, attr, ctrl, transclude) {
    var _columnLength;
    var _columnTrack = 0;
    var columns = [];
    _columnLength = parseInt(scope.columns);

    // Create columns from row's attribute
    for (i = 0; i < _columnLength; i++) {
      var column = angular.element(document.createElement("div"));

      column.addClass("grid-column");

      columns.push(column);
    }

    // Fill all columns with the passed collection scope
    fillColumns();

    // Render all columns in the DOM
    renderColumns(element, columns);

    function renderColumns(el, columns) {
      for (let col of columns) {
        el.append(col);
      }
    }

    function fillColumns() {
      var collection = scope.collection;

      // Compile multiple transcluded content for each collection item
      var idx = 0;
      var template;
      
      // fetch template
      transclude(scope, function(clone, innerScope, element) {
        template = clone;
        innerScope = innerScope;
      });
      
      for (let data of collection) {
        var dataAttr = `collection[${idx++}]`;
        var item = template.clone();
        item.attr("data", dataAttr);
        
        $compile(item)(scope);

        // Append the compiled clone into its respective column
        appendColumn(item);
      }
    }

    function appendColumn(data) {
      columns[_columnTrack].append(data);

      if (_columnTrack < _columnLength - 1) {
        _columnTrack++;
      } else {
        _columnTrack = 0;
      }
    }
  }
});
* {
  font-family: "Raleway", sans-serif;
}

body {
  display: flex;
  flex-direction: row;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: stretch;
  align-content: stretch;
}

grid {
  display: flex;
}

.blog-preview {
  width: 280px;
  margin: 15px 15px 60px;
  display: flex;
  flex-direction: column;
}

.blog-preview > .blog-thumbnail {
  width: 100%;
  height: 190px;
  background: #181D20;
}

.blog-preview > .blog-wrapper {
  display: flex;
  flex-direction: column;
}

.blog-preview > .blog-wrapper > .blog-head {
  height: 55px;
  margin: 6px 8px;
}

.blog-preview > .blog-wrapper > .blog-head > .blog-title {
  margin: 2px 0 0 0;
}

.blog-preview > .blog-wrapper > .blog-head > p {
  font-size: 10px;
  margin: 0 0 3px 0;
}

.blog-preview > .blog-wrapper > .blog-actions {
  height: 16px;
  background: rgba(24, 29, 32, 0.5);
}

.blog-preview > .blog-wrapper > .blog-content {
  display: flex;
  flex-direction: column;
}

.blog-preview > .blog-wrapper > .blog-content p {
  margin: 16px 10px;
}

.blog-preview > .blog-wrapper > .blog-content > .blog-access {
  text-decoration: none;
  text-align: center;
  vertical-align: middle;
  width: 100px;
  height: 25px;
  line-height: 25px;
  margin: auto;
  color: #FFF;
  background: #3E515A;
}
<body ng-app="app" ng-controller="testController">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.5.0/angular.min.js"></script>
  
  <grid rows="3" collection="blogs">
    <blog></blog>
  </grid>

</body>

答案 1 :(得分:0)

我已经做了一个解决方法,我不认为它实际上是一个很好的方法,但至少得到它我正在寻找的方式..我将把它作为临时方法。

所以我在这个transclude函数中的解决方法是这样的:

function fillColumns(){
    var collection = scope.collection;

    // Compile multiple transcluded content for each collection item
    for(let data of collection){
        transclude(scope.$new(), function(clone, innerScope){
            // Set this property to test if the compiled directive recieves it
            innerScope.data = data; //This is what has changed

            $compile(clone)(innerScope);

            // Append the compiled clone into its respective column
            appendColumn(clone);
        });
    }
}

然后使用$scope.$parent.data

从指令范围访问该数据
function controller($scope){
    $scope.data = $scope.$parent.data;
}