供设计师使用的AngularJS自包含指令

供设计师使用的AngularJS自包含指令,angularjs,angularjs-directive,Angularjs,Angularjs Directive,我正在使用一个web应用程序,它允许设计人员通过编写html并结合我和其他开发人员创建的angularjs指令来创建页面。我正在努力寻找用数据填充指令的最佳方法 最初的尝试是使所有指令完全独立。因此,例如,产品页面可能如下所示(为清晰起见,所有这些页面都有自定义html): 如果指令需要数据(几乎所有指令都需要),它们将使用服务调用web API并获取所需的数据。这种方法产生了一些问题 指令通常需要来自父级或同级的一些信息。在下面的示例中,product image可能需要ProductID

我正在使用一个web应用程序,它允许设计人员通过编写html并结合我和其他开发人员创建的angularjs指令来创建页面。我正在努力寻找用数据填充指令的最佳方法

最初的尝试是使所有指令完全独立。因此,例如,产品页面可能如下所示(为清晰起见,所有这些页面都有自定义html):


如果指令需要数据(几乎所有指令都需要),它们将使用服务调用web API并获取所需的数据。这种方法产生了一些问题

  • 指令通常需要来自父级或同级的一些信息。在下面的示例中,product image可能需要ProductID才能获得正确的映像。在这种情况下,我必须依赖于从querystring参数或存储在最初由父指令填充的angularJS服务中获取信息
  • 很多API调用。每一条指令都有自己的API调用,我现在要加载的页面超过15个API调用,而且随着时间的推移,这些调用会不断增加。尽管许多数据可能密切相关(即使在同一个数据库表中)。显然,这并不理想
  • 所以我已经开始改变我的第二次通过的方法。现在,这些指令被设置为一个树结构,每个指令都希望它的数据需求通过一个属性传递进来。下面是一个例子:

    <product-image product-url="vm.product.imageUrl" ng-if="vm.product"></product-image>
    
    
    
    这解决了#2太多API和数据库调用的问题,但向设计器公开了太多的内部构件。现在,设计师必须知道如何传递产品url,并且必须了解幕后有一个vm.product。他甚至可能需要理解一些angularJS(ng if)。我已经看到这个模式在Angular2中使用了很多,甚至有输入。对于开发人员使用来说似乎很好,但对于设计器使用的指令来说则不然。我们希望隐藏内部工作和复杂性,同时赋予设计器控制布局的能力

    最后,我正在考虑使用父控制器来填充页面上可能需要的所有内容。然后所有的子指令都会像现在一样使用服务,但是数据已经加载,而不是调用API。这些指令仍然很简单,大部分是自包含的,但它们的数据加载是由父级触发的。我遇到的唯一问题是,由于设计器没有使用指令,我们可能会加载大量未使用的数据。但我觉得这是一个必要的权衡


    是否有人构建了类似的东西,我缺少了哪些可能的方法?

    您可以在服务中使用组件树。 从设计师友好型开始:

    <div ng-app="MyApp">
      <product id="1">
        <product-image></product-image>
      </product>
    </div>
    
    
    
    类似这样的分类是有效的:

    angular.module('MyApp', [])
    
    .factory('api', function($q){
      return {
        loadProduct: function(id){
          return $q.when({
            id: id,
        imageUrl: 'http://i2.cdn.turner.com/cnnnext/dam/assets/160407085910-setsuna-main-overlay-tease.jpg'
      })
        }
      };  
    })
    .component('product', {
      transclude: true,
      bindings: {
        id: '=' 
      },
      template: [
        '<div ng-transclude></div>'
      ].join(''),
      controller: function(api) {
        var self = this;
        this.$onInit = function() {
          self.data = api.loadProduct(this.id);
        };
      }
    })
    .component('productImage', {
      require: {
        product: '^product'
      },  
      bindings: {
      },
      template: [
        '<pre>{{ $ctrl.url | json }}</pre>'
      ].join(''),
      controller: function() {
        var self = this;
        this.url = false;
        this.$onInit = function() {
          this.product.data.then(function(data){
            self.url = data.imageUrl;
          })
        };
      }
    })
    
    angular.module('MyApp',[])
    .factory('api',函数($q){
    返回{
    loadProduct:函数(id){
    返回$q.when({
    id:id,
    imageUrl:'http://i2.cdn.turner.com/cnnnext/dam/assets/160407085910-setsuna-main-overlay-tease.jpg'
    })
    }
    };  
    })
    .组件(“产品”{
    是的,
    绑定:{
    id:“=”
    },
    模板:[
    ''
    ].加入(“”),
    控制器:函数(api){
    var self=这个;
    这是。$onInit=function(){
    self.data=api.loadProduct(this.id);
    };
    }
    })
    .component('productImage'{
    要求:{
    产品:“^product”
    },  
    绑定:{
    },
    模板:[
    “{{$ctrl.url | json}}”
    ].加入(“”),
    控制器:函数(){
    var self=这个;
    this.url=false;
    这是。$onInit=function(){
    this.product.data.then(函数(数据){
    self.url=data.imageUrl;
    })
    };
    }
    })
    
    请参阅此代码笔:

    我认为作为模型并处理所有API的服务没有问题。这种方法有什么问题?这个问题在当前状态下太模糊了。我在产品页面示例中添加了更多标记。那么,你会让产品控制器加载服务/模型中的所有数据,让孩子们使用这些数据吗?我会让服务完全基于承诺,更加灵活和一致$http承诺在第一次请求时缓存在服务中,在下一次请求时返回,并在需要时重新验证。使用状态/路由解析程序包装服务可能是有益的,因为解析程序注入承诺值,无需手动打开承诺。我喜欢此解决方案,但我看到的问题是,您失去了与父产品数据的双向绑定。例如,假设您在中更新数据,然后在中提交产品数据,您在中所做的任何更改仅存在于该指令中。不一定。。。有很多方法可以保持这些作用域的链接。。。(接受的答案?你想让我进一步调查吗?)
    angular.module('MyApp', [])
    
    .factory('api', function($q){
      return {
        loadProduct: function(id){
          return $q.when({
            id: id,
        imageUrl: 'http://i2.cdn.turner.com/cnnnext/dam/assets/160407085910-setsuna-main-overlay-tease.jpg'
      })
        }
      };  
    })
    .component('product', {
      transclude: true,
      bindings: {
        id: '=' 
      },
      template: [
        '<div ng-transclude></div>'
      ].join(''),
      controller: function(api) {
        var self = this;
        this.$onInit = function() {
          self.data = api.loadProduct(this.id);
        };
      }
    })
    .component('productImage', {
      require: {
        product: '^product'
      },  
      bindings: {
      },
      template: [
        '<pre>{{ $ctrl.url | json }}</pre>'
      ].join(''),
      controller: function() {
        var self = this;
        this.url = false;
        this.$onInit = function() {
          this.product.data.then(function(data){
            self.url = data.imageUrl;
          })
        };
      }
    })