如何从ember的子组件范围访问父组件范围?

大卫·尼尔森

我很好奇这在炭烬中是否有可能。这是很容易做到的角度(plunkr:http : //plnkr.co/edit/O2e0ukyXdKMs4FcgKGmX?p=preview ):

目标是为api使用者提供易于使用的,通用的,可重用的手风琴api。

我希望调用者能够使用的api是这样的(就像有角度的api一样):

{{#ember-accordion listOfAccordionPaneObjects=model}}

  {{#ember-accordion-heading}}
     heading template html {{accordionPaneObject.firstName}}
  {{/ember-accordion-heading}}

  {{#ember-accordion-body}}
     this is the accordion body  {{accordionPaneObject.lastName}}
  {{/ember-accordion-body}}

{{/ember-accordion}}

这是我使用angular编写的一个工作示例:

<!doctype html>
<html ng-app="angular-accordion">
<head>
    <style>
        .angular-accordion-header {
            background-color: #999;
            color: #ffffff;
            padding: 10px;
            margin: 0;
            line-height: 14px;
            -webkit-border-top-left-radius: 5px;
            -webkit-border-top-right-radius: 5px;
            -moz-border-radius-topleft: 5px;
            -moz-border-radius-topright: 5px;
            border-top-left-radius: 5px;
            border-top-right-radius: 5px;
            cursor: pointer;
            text-decoration: none;
            font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
            font-size: 14px;
        }

        .angular-accordion-container {
            height: 100%;
            width: 100%;
        }

        .angular-accordion-pane {
            padding: 2px;
        }

        .angularaccordionheaderselected {
            background-color: #bbb;
            color: #333;
            font-weight: bold;
        }

        .angular-accordion-header:hover {
            text-decoration: underline !important;
        }

        .angularaccordionheaderselected:hover {
            text-decoration: underline !important;
        }

        .angular-accordion-pane-content {
            padding: 5px;
            overflow-y: auto;
            border-left: 1px solid #bbb;
            border-right: 1px solid #bbb;
            border-bottom: 1px solid #bbb;
            -webkit-border-bottom-left-radius: 5px;
            -webkit-border-bottom-right-radius: 5px;
            -moz-border-radius-bottomleft: 5px;
            -moz-border-radius-bottomright: 5px;
            border-bottom-left-radius: 5px;
            border-bottom-right-radius: 5px;
        }

        .angulardisabledpane {
            opacity: .2;
        }
    </style>
</head>
<body style="margin: 0;">


<div style="height: 90%; width: 100%; margin: 0;" ng-controller="outerController">

    <angular-accordion list-of-accordion-pane-objects="outerControllerData">
        <pane>
            <pane-header>Header {{accordionPaneObject}}</pane-header>
            <pane-content>Content {{accordionPaneObject}}</pane-content>
        </pane>
    </angular-accordion>

</div>

    <script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.js"></script>
    <script>
        angular.module('angular-accordion', [])
                .directive('angularAccordion', function() {
                    var template = '';

                    return {
                        restrict: 'E',
                        transclude: true,
                        replace: true,
                        template: '<div>' +
                                        '<div ng-transclude class="angular-accordion-container" ng-repeat="accordionPaneObject in listOfAccordionPaneObjects"></div>' +
                                  '</div>',
                        controller: ['$scope', function($scope) {
                            var panes = [];

                            this.addPane = function(pane) {
                                panes.push(pane);
                            };
                        }],
                        scope: {
                            listOfAccordionPaneObjects: '='
                        }
                    };
                })
                .directive('pane', function() {
                    return {
                        restrict: 'E',
                        transclude: true,
                        replace: true,
                        template: '<div ng-transclude class="angular-accordion-pane"></div>'
                    };
                })
                .directive('paneHeader', function() {
                    return {
                        restrict: 'E',
                        require: '^angularAccordion',
                        transclude: true,
                        replace: true,
                        link: function(scope, iElement, iAttrs, controller) {
                            controller.addPane(scope);

                            scope.toggle = function() {
                                scope.expanded = !scope.expanded;
                            };
                        },
                        template: '<div ng-transclude class="angular-accordion-header" ng-click="toggle()"></div>'
                    };
                })
                .directive('paneContent', function() {
                    return {
                        restrict: 'EA',
                        require: '^paneHeader',
                        transclude: true,
                        replace: true,
                        template: '<div ng-transclude class="angular-accordion-pane-content" ng-show="expanded"></div>'
                    };
                })
                .controller('outerController', ['$scope', function($scope) {
                    $scope.outerControllerData = [1, 2, 3];
                }]);
    </script>
</body>
</html>

这就是我坚持使用余烬做同样的事情的地方:

index.html

<!DOCTYPE html>
<html>
    <body>
        <script src="//cdnjs.cloudflare.com/ajax/libs/require.js/2.1.9/require.js" data-main="main.js"></script>
    </body>
</html>

main.js

require.config({
    paths: {
        'ember': 'bower_components/ember/ember',
        'handlebars': 'bower_components/handlebars/handlebars',
        'jquery': 'bower_components/jquery/jquery',
        'text': 'bower_components/requirejs-text/text'
    },
    shim: {
        ember: {
            deps: ['jquery', 'handlebars'],
            exports: 'Ember'
        }
    }
});

define(function(require) {
    var Ember = require('ember'),
        EmberAccordionComponent = require('src/EmberAccordionComponent'),
        EmberAccordionTemplate = require('text!templates/ember-accordion.hbs'),
        EmberAccordionHeaderTemplate = require('text!templates/ember-accordion-header.hbs'),
        EmberAccordionBodyTemplate = require('text!templates/ember-accordion-body.hbs'),
        ApplicationTemplate = require('text!templates/application.hbs'),
        IndexTemplate = require('text!templates/index.hbs');

    var App = Ember.Application.create({
        LOG_STACKTRACE_ON_DEPRECATION : true,
        LOG_BINDINGS                  : true,
        LOG_TRANSITIONS               : true,
        LOG_TRANSITIONS_INTERNAL      : true,
        LOG_VIEW_LOOKUPS              : true,
        LOG_ACTIVE_GENERATION         : true
    });

    Ember.TEMPLATES = {};
    Ember.TEMPLATES['application'] = Ember.Handlebars.compile(ApplicationTemplate);
    Ember.TEMPLATES['index'] = Ember.Handlebars.compile(IndexTemplate);
    Ember.TEMPLATES['components/ember-accordion'] = Ember.Handlebars.compile(EmberAccordionTemplate);
    Ember.TEMPLATES['components/ember-accordion-header'] = Ember.Handlebars.compile(EmberAccordionHeaderTemplate);
    Ember.TEMPLATES['components/ember-accordion-body'] = Ember.Handlebars.compile(EmberAccordionBodyTemplate);

    App.EmberAccordionComponent = EmberAccordionComponent;

    App.IndexRoute = Ember.Route.extend({
        model: function() {
            return [
                {
                    name: 'Bob'
                },
                {
                    name: 'Jill'
                }]
        }
    })
});

EmberAccordionComponent.js

define(function(require) {
    require('ember');

    var EmberAccordionComponent = Ember.Component.extend({});

    return EmberAccordionComponent;
});

application.hbs

{{outlet}}

ember-accordion-header.hbs

<div style="color: blue;">
    {{yield}}
</div>

ember-accordion-body.hbs

<div style="color: green;">
    {{yield}}
</div>

index.hbs

{{#ember-accordion listOfAccordionPaneObjects=model}}
    {{#ember-accordion-header}}
        {{log this.constructor}}
        {{log this}}
        Header {{accordionPaneObject.name}}
    {{/ember-accordion-header}}
    {{#ember-accordion-body}}
        Body {{accordionPaneObject.name}}
    {{/ember-accordion-body}}
{{/ember-accordion}}

ember-accordion.hbs

{{#each accordionPaneObject in listOfAccordionPaneObjects}}
    {{yield}}
{{/each}}

--

这很难调试。因此输入:

{{log this.constructor}}

和:

{{log this}}

进入:

{{#ember-accordion-header}}

输出以下内容:

  • Class.model =未定义(为什么?)
  • Ember.ArrayController

我已尝试按照本文(http://www.thesoftwaresimpleton.com/blog/2013/11/21/component-block/)的建议重写Ember.Component的_yield私有方法

var EmberAccordionHeaderComponent = Ember.Component.extend({
    _yield: function(context, options) {
        var get = Ember.get,
            view = options.data.view,
            parentView = this._parentView,
            template = get(this, 'template');

        if (template) {
            Ember.assert("A Component must have a parent view in order to yield.", parentView);
            view.appendChild(Ember.View, {
                isVirtual: true,
                tagName: '',
                _contextView: parentView,
                template: template,
                context: get(view, 'context'), // the default is get(parentView, 'context'),
                controller: get(view, 'controller'), // the default is get(parentView, 'context'),
                templateData: { keywords: parentView.cloneKeywords() }
            });
        }
    }
});

但是当我这样做时,我仍然无法在子组件范围内访问AccordionPaneObject,并且我的{{log this.constructor}}现在指向:.EmberAccordionHeaderComponent

因此,看来我要到达某个地方,我只需要再上一层。

当我尝试在EmberAccordionHeaderComponent.js中使用此代码时:

var EmberAccordionHeaderComponent = Ember.Component.extend({
    _yield: function(context, options) {
        var get = Ember.get,
            view = options.data.view,
            parentView = this._parentView,
            grandParentView = this._parentView._parentView,
            template = get(this, 'template');

        if (template) {
            Ember.assert("A Component must have a parent view in order to yield.", parentView);
            view.appendChild(Ember.View, {
                isVirtual: true,
                tagName: '',
                _contextView: parentView,
                template: template,
                context: get(grandParentView, 'context'), // the default is get(parentView, 'context'),
                controller: get(grandParentView, 'controller'), // the default is get(parentView, 'context'),
                templateData: { keywords: parentView.cloneKeywords() }
            });
        }
    }
});

我仍然无法访问AccordionPaneObject,但是现在我看到{{log this.constructor}}输出.EmberAccordionComponent。看来我在正确的范围内,但数据仍未绑定。

有趣的是,如果我在覆盖的_yield中使用了重新分配上下文和控制器的任何变体,则可以使用以下命令在控制台中访问所需的数据:

this._parentView._context.content
Marcio Junior

我用一些注释更新了您的代码,请看一下http://emberjs.jsbin.com/ivOyiZa/1/edit

Java脚本

App = Ember.Application.create();

App.IndexRoute = Ember.Route.extend({
  model: function() {
    return [
      { head: "foo head", body: "foo body " },
      { head: "bar head", body: "bar body " },
      { head: "ya head", body: "yo body " }
    ];
  }
});

App.EmberAccordionComponent = Ember.Component.extend({
  // each accordion header/body item, will have a instance of that view.
  // so we can isolate the expanded state for each accordion header/body
  emberAccordionItemView: Ember.View.extend({    
    expanded: false
  }),
  _yield: function(context, options) {
    var get = Ember.get, 
    view = options.data.view,
    parentView = this._parentView,
    template = get(this, 'template');

    if (template) {
      Ember.assert("A Component must have a parent view in order to yield.", parentView);      
      view.appendChild(Ember.View, {
        isVirtual: true,
        tagName: '',
        _contextView: parentView,
        template: template,
        context: get(view, 'context'), // the default is get(parentView, 'context'),
        controller: get(view, 'controller'), // the default is get(parentView, 'context'),
        templateData: { keywords: parentView.cloneKeywords() }
      });
    }
  }
});

App.EmberAccordionHeaderComponent = Ember.Component.extend({  
  classNames: ['ember-accordion-header'],  
  click: function() {
    // here we toggle the emberAccordionItemView.expanded property
    this.toggleProperty('parentView.expanded');  
  }
});

范本

  <script type="text/x-handlebars" data-template-name="index">
    {{#ember-accordion listOfAccordionPaneObjects=model}}                        
          {{#ember-accordion-header}}
              {{head}} <!-- each object passed in listOfAccordionPaneObjects=model can be accessed here -->
          {{/ember-accordion-header}}
          {{#ember-accordion-body}}
              {{body}} <!-- each object passed in listOfAccordionPaneObjects=model can be accessed here -->
          {{/ember-accordion-body}}        
    {{/ember-accordion}}        
  </script>

  <script type="text/x-handlebars" data-template-name="components/ember-accordion">     
    {{#each listOfAccordionPaneObjects itemViewClass="view.emberAccordionItemView"}}            
      <div class="ember-accordion-container">
        <div class="ember-accordion-pane">          
            {{yield}}          
        </div>
      </div>      
    {{/each}}
  </script>

  <script type="text/x-handlebars" data-template-name="components/ember-accordion-header">        
    {{yield}}    
  </script>

  <script type="text/x-handlebars" data-template-name="components/ember-accordion-body">    
    <!-- when EmberAccordionHeaderComponent.click is called, the expanded property change and the content can be visible or not, based on expanded truth -->
    {{#if parentView.expanded}}
      <div class="ember-accordion-pane-content">
        {{yield}}
      </div>
    {{/if}}
  </script>

的CSS

.ember-accordion-header {
  background-color: #999;
  color: #ffffff;
  padding: 10px;
  margin: 0;
  line-height: 14px;
  -webkit-border-top-left-radius: 5px;
  -webkit-border-top-right-radius: 5px;
  -moz-border-radius-topleft: 5px;
  -moz-border-radius-topright: 5px;
  border-top-left-radius: 5px;
  border-top-right-radius: 5px;
  cursor: pointer;
  text-decoration: none;
  font-family: "Helvetica Neue", Helvetica, Arial, sans-serif;
  font-size: 14px;
}

.ember-accordion-container {
  height: 100%;
  width: 100%;
}

.ember-accordion-pane {
  padding: 2px;
}

.emberaccordionheaderselected {
  background-color: #bbb;
  color: #333;
  font-weight: bold;
}

.ember-accordion-header:hover {
  text-decoration: underline !important;
}

.emberaccordionheaderselected:hover {
  text-decoration: underline !important;
}

.ember-accordion-pane-content {
  padding: 5px;
  overflow-y: auto;
  border-left: 1px solid #bbb;
  border-right: 1px solid #bbb;
  border-bottom: 1px solid #bbb;
  -webkit-border-bottom-left-radius: 5px;
  -webkit-border-bottom-right-radius: 5px;
  -moz-border-radius-bottomleft: 5px;
  -moz-border-radius-bottomright: 5px;
  border-bottom-left-radius: 5px;
  border-bottom-right-radius: 5px;
}

.emberdisabledpane {
  opacity: .2;
}

本文收集自互联网,转载请注明来源。

如有侵权,请联系[email protected] 删除。

编辑于
0

我来说两句

0条评论
登录后参与评论

相关文章

来自分类常见问题

angularJS:如何在父范围中调用子范围函数

来自分类Dev

如何访问Javascript对象内的父范围?

来自分类Dev

Dagger2:使子组件的子组件可以访问父组件依赖项

来自分类Dev

如何从Angular2的父组件类访问子组件类?

来自分类Dev

如何将父组件注入子组件?

来自分类Dev

Angular 2-子组件如何与父组件说话?

来自分类Dev

父组件的angular 2访问子组件属性

来自分类Dev

如何在不通过输入传递子组件的情况下从子组件访问父组件的值?

来自分类Dev

如何从Vue.js的子组件访问父方法?

来自分类Dev

如何在父组件中访问子组件的引用

来自分类Dev

访问父组件变量

来自分类Dev

如何从组件访问父HTML元素?

来自分类Dev

使用导航组件在父片段范围内共享ViewModel

来自分类Dev

如何在Angular 9中从子组件访问父组件

来自分类Dev

如何从ReactJs的父组件访问子组件的输入值?

来自分类Dev

访问父组件中的子数据

来自分类Dev

访问b表插槽中的父组件范围

来自分类Dev

如何访问在父模块中声明的组件

来自分类Dev

如何设置子组件的子组件的父组件状态

来自分类Dev

ReactJS:如何访问父组件中的所有子组件?

来自分类Dev

如何访问子组件中的父数据

来自分类Dev

在Ember中完成Ember.run.later之后,如何确保子组件调用父函数

来自分类Dev

访问父组件变量

来自分类Dev

当父组件使用ng-content Angular时,子组件的可访问性父组件

来自分类Dev

如何与 MdDialog 内的组件共享范围服务?

来自分类Dev

正确使用组件和子组件访问父状态

来自分类Dev

访问 ... 子组件的父组件的父组件的 html 元素

来自分类Dev

react-Native:访问父组件内的子组件功能

来自分类Dev

如何从导航组件中的子片段访问父片段的视图

Related 相关文章

  1. 1

    angularJS:如何在父范围中调用子范围函数

  2. 2

    如何访问Javascript对象内的父范围?

  3. 3

    Dagger2:使子组件的子组件可以访问父组件依赖项

  4. 4

    如何从Angular2的父组件类访问子组件类?

  5. 5

    如何将父组件注入子组件?

  6. 6

    Angular 2-子组件如何与父组件说话?

  7. 7

    父组件的angular 2访问子组件属性

  8. 8

    如何在不通过输入传递子组件的情况下从子组件访问父组件的值?

  9. 9

    如何从Vue.js的子组件访问父方法?

  10. 10

    如何在父组件中访问子组件的引用

  11. 11

    访问父组件变量

  12. 12

    如何从组件访问父HTML元素?

  13. 13

    使用导航组件在父片段范围内共享ViewModel

  14. 14

    如何在Angular 9中从子组件访问父组件

  15. 15

    如何从ReactJs的父组件访问子组件的输入值?

  16. 16

    访问父组件中的子数据

  17. 17

    访问b表插槽中的父组件范围

  18. 18

    如何访问在父模块中声明的组件

  19. 19

    如何设置子组件的子组件的父组件状态

  20. 20

    ReactJS:如何访问父组件中的所有子组件?

  21. 21

    如何访问子组件中的父数据

  22. 22

    在Ember中完成Ember.run.later之后,如何确保子组件调用父函数

  23. 23

    访问父组件变量

  24. 24

    当父组件使用ng-content Angular时,子组件的可访问性父组件

  25. 25

    如何与 MdDialog 内的组件共享范围服务?

  26. 26

    正确使用组件和子组件访问父状态

  27. 27

    访问 ... 子组件的父组件的父组件的 html 元素

  28. 28

    react-Native:访问父组件内的子组件功能

  29. 29

    如何从导航组件中的子片段访问父片段的视图

热门标签

归档