如何从一个灰烬子组件范围内访问父组件的范围? [英] how to access parent component scope from a child components scope in ember?

查看:191
本文介绍了如何从一个灰烬子组件范围内访问父组件的范围?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我很好奇,如果这甚至有可能在灰烬。这是一件容易的事情在角(plunkr做:<一href=\"http://plnkr.co/edit/O2e0ukyXdKMs4FcgKGmX?p=$p$pview\">http://plnkr.co/edit/O2e0ukyXdKMs4FcgKGmX?p=$p$pview ):

我们的目标是使一个易于使用的,通用的,可重复使用的手风琴的API API的消费者。

我想要的主叫方能够使用该API是这样的(就像角API):

  {{#烬,手风琴listOfAccordionPaneObjects =模型}}  {{#烬,手风琴标题}}
     标题HTML模板{{accordionPaneObject.firstName}}
  {{/烬-手风琴标题}}  {{#烬手风琴体}}
     这是手风琴身体{{accordionPaneObject.lastName}}
  {{/烬-手风琴体}}{{/烬,手风琴}}

下面是我使用的角度写了一个工作的例子:

 &LT;!DOCTYPE HTML&GT;
&LT; HTML NG-应用=角手风琴&GT;
&LT; HEAD&GT;
    &LT;风格&GT;
        .angular手风琴头{
            背景颜色:#999;
            颜色:#FFFFFF;
            填充:10px的;
            保证金:0;
            行高:14px的;
            -webkit-边框左上角的半径:5像素;
            -webkit-边框右上角半径:5像素;
            -moz-边界半径-左上:5像素;
            -moz-边界半径-topright:5像素;
            边框左上角的半径:5像素;
            边框右上角半径:5像素;
            光标:指针;
            文字修饰:无;
            FONT-FAMILY:Helvetica Neue字体,黑体,宋体,无衬线;
            字体大小:14px的;
        }        .angular手风琴容器{
            高度:100%;
            宽度:100%;
        }        .angular手风琴窗格{
            填充:2px的;
        }        .angularaccordionheaderselected {
            背景色:#bbb;
            颜色:#333;
            字体重量:大胆的;
        }        .angular-手风琴头:悬停{
            文字修饰:强调重要的;!
        }        .angularaccordionheaderselected:悬停{
            文字修饰:强调重要的;!
        }        .angular手风琴窗格内容{
            填充:5像素;
            溢出-Y:汽车;
            左边框:1px的固体#bbb;
            右边框:1px的固体#bbb;
            下边框:1px的固体#bbb;
            -webkit-边框左下角半径:5像素;
            -webkit-边框右下半径:5像素;
            -moz-边界半径-BOTTOMLEFT:5像素;
            -moz-边界半径-bottomright:5像素;
            边框左下角半径:5像素;
            边框右下角半径:5像素;
        }        .angulardisabledpane {
            不透明度:0.2;
        }
    &LT; /风格&GT;
&LT; /头&GT;
&LT;车身风格=保证金:0;&GT;
&LT; D​​IV的风格=高度:90%;宽度:100%;保证金:0; NG-控制器=outerController&GT;    &LT;角手风琴列表的的手风琴窗格对象=outerControllerData&GT;
        &LT;&窗格GT;
            &LT;窗​​格中头&GT;头{{accordionPaneObject}}&LT; /窗格中头&GT;
            &LT;窗​​格中的内容&gt;内容{{accordionPaneObject}}&LT; /窗格中的内容&GT;
        &LT; /窗格&GT;
    &LT; /角手风琴&GT;&LT; / DIV&GT;    &所述; SCRIPT SRC =htt​​ps://ajax.googleapis.com/ajax/libs/angularjs/1.2.1/angular.js&GT;&下; /脚本&GT;
    &LT;脚本&GT;
        angular.module('角手风琴,[])
                .directive('angularAccordion',函数(){
                    VAR模板='';                    返回{
                        限制:'E',
                        transclude:真实,
                        更换:真实,
                        模板:'&LT; D​​IV&GT;' +
                                        '&LT; D​​IV NG-transclude类=角手风琴容器NG重复=accordionPaneObject在listOfAccordionPaneObjects&GT;&LT; / DIV&GT;' +
                                  '&LT; / DIV&GT;,
                        控制器:['$范围',函数($范围){
                            变种窗格= [];                            this.addPane =功能(窗格){
                                panes.push(面板);
                            };
                        }],
                        范围: {
                            listOfAccordionPaneObjects:'='
                        }
                    };
                })
                .directive('面板',函数(){
                    返回{
                        限制:'E',
                        transclude:真实,
                        更换:真实,
                        模板:'&LT; D​​IV NG-transclude类=角手风琴窗格&GT;&LT; / DIV&GT;'
                    };
                })
                .directive('paneHeader',函数(){
                    返回{
                        限制:'E',
                        要求:'^ angularAccordion',
                        transclude:真实,
                        更换:真实,
                        链接:功能(范围,iElement,iAttrs,控制器){
                            controller.addPane(范围);                            scope.toggle =功能(){
                                !scope.expanded = scope.expanded;
                            };
                        },
                        模板:'&LT; D​​IV NG-transclude类=角手风琴头NG点击=切换()&GT;&LT; / DIV&GT;'
                    };
                })
                .directive('paneContent',函数(){
                    返回{
                        限制:EA,
                        要求:'^ paneHeader',
                        transclude:真实,
                        更换:真实,
                        模板:'&LT; D​​IV NG-transclude类=角手风琴窗格内容NG秀=扩大&GT;&LT; / DIV&GT;'
                    };
                })
                .controller('outerController',['$范围',函数($范围){
                    $ scope.outerControllerData = [1,2,3];
                }]);
    &LT; / SCRIPT&GT;
&LT; /身体GT;
&LT; / HTML&GT;

在此处,我坚持做同样与Ember:

index.html的

 &LT;!DOCTYPE HTML&GT;
&LT; HTML和GT;
    &LT;身体GT;
        &LT;脚本的src =// cdnjs.cloudflare.com/ajax/libs/require.js/2.1.9/require.js数据主要=main.js&GT;&LT; / SCRIPT&GT;
    &LT; /身体GT;
&LT; / HTML&GT;

main.js

  require.config({
    路径:{
        烬:bower_components /烬/烬,
        '把手':'bower_components /车把/把手',
        'jQuery的':'bower_components / jQuery的/ jQuery的,
        文:bower_components / requirejs文/文
    },
    垫片:{
        烬:{
            DEPS:['jquery的','把手'],
            出口:余烬
        }
    }
});定义(功能(需要){
    VAR灰烬=要求(余烬),
        EmberAccordionComponent =要求('SRC / EmberAccordionComponent'),
        EmberAccordionTemplate =要求('的文字!模板/烬-accordion.hbs'),
        EmberAccordionHeaderTemplate =要求('的文字!模板/烬-手风琴header.hbs'),
        EmberAccordionBodyTemplate =要求('的文字!模板/烬-手风琴body.hbs'),
        ApplicationTemplate =要求('的文字!模板/ application.hbs'),
        IndexTemplate =要求('文本模板/ index.hbs!');    VAR应用= Ember.Application.create({
        LOG_STACKTRACE_ON_DE preCATION:真实,
        LOG_BINDINGS:真实,
        LOG_TRANSITIONS:真实,
        LOG_TRANSITIONS_INTERNAL:真实,
        LOG_VIEW_LOOKUPS:真实,
        LOG_ACTIVE_GENERATION:真
    });    Ember.TEMPLATES = {};
    Ember.TEMPLATES ['应用'] = Ember.Handlebars.compile(ApplicationTemplate);
    Ember.TEMPLATES ['索引'] = Ember.Handlebars.compile(IndexTemplate);
    Ember.TEMPLATES ['组件/烬-手风琴'] = Ember.Handlebars.compile(EmberAccordionTemplate);
    Ember.TEMPLATES ['组件/烬-手风琴头'] = Ember.Handlebars.compile(EmberAccordionHeaderTemplate);
    Ember.TEMPLATES ['组件/烬-手风琴体'] = Ember.Handlebars.compile(EmberAccordionBodyTemplate);    App.EmberAccordionComponent = EmberAccordionComponent;    App.IndexRoute = Ember.Route.extend({
        型号:功能(){
            返回[
                {
                    名称:'鲍勃'
                },
                {
                    名称:'吉尔'
                }]
        }
    })
});

EmberAccordionComponent.js

 定义(功能(需要){
    需要('烬');    变种EmberAccordionComponent = Ember.Component.extend({});    返回EmberAccordionComponent;
});

application.hbs

  {{口}}

烬-手风琴header.hbs

 &LT; D​​IV的风格=颜色:蓝色;&GT;
    {{产量}}
&LT; / DIV&GT;

烬-手风琴body.hbs

 &LT; D​​IV的风格=颜色:绿色;&GT;
    {{产量}}
&LT; / DIV&GT;

index.hbs

  {{#烬,手风琴listOfAccordionPaneObjects =模型}}
    {{#烬手风琴头}}
        {{登录this.constructor}}
        {{登录此}}
        头{{accordionPaneObject.name}}
    {{/烬-手风琴头}}
    {{#烬手风琴体}}
        身体{{accordionPaneObject.name}}
    {{/烬-手风琴体}}
{{/烬,手风琴}}

烬-accordion.hbs

  {{#每个accordionPaneObject在listOfAccordionPaneObjects}}
    {{产量}}
{{/每}}

-

这是棘手的调试。所以在投入:

  {{登录this.constructor}}

  {{记录此}}

  {{#烬手风琴头}}

输出如下:


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

  • Ember.ArrayController

我试过重写Ember.Component的私人_yield方法这篇文章(<一所建议href=\"http://www.thesoftwaresimpleton.com/blog/2013/11/21/component-block/\">http://www.thesoftwaresimpleton.com/blog/2013/11/21/component-block/ ):

  VAR EmberAccordionHeaderComponent = Ember.Component.extend({
    _yield:功能(背景下,期权){
        VAR GET = Ember.get,
            鉴于= options.data.view,
            parentView = this._parentView,
            模板=得到(这一点,'模板');        如果(模板){
            Ember.assert(A组分必须以得到具有父视图,parentView);
            view.appendChild(Ember.View,{
                isVirtual:真实,
                标签名:'',
                _contextView:parentView,
                模板:模板,
                背景:获得(查看,语境),//默认为得到(parentView,语境),
                控制器:得到(查看,控制器),//默认为得到(parentView,语境),
                templateData:{关键字:parentView.cloneKeywords()}
            });
        }
    }
});

但我这样做的时候我还没有在我的子组件范围访问accordionPaneObject,我的{{登录this.constructor}}现在指向:.EmberAccordionHeaderComponent

因此​​,它看起来像我要去哪里,我只需要走一个多水平了。

当我尝试使用此code在EmberAccordionHeaderComponent.js:

  VAR EmberAccordionHeaderComponent = Ember.Component.extend({
    _yield:功能(背景下,期权){
        VAR GET = Ember.get,
            鉴于= options.data.view,
            parentView = this._parentView,
            grandParentView = this._parentView._parentView,
            模板=得到(这一点,'模板');        如果(模板){
            Ember.assert(A组分必须以得到具有父视图,parentView);
            view.appendChild(Ember.View,{
                isVirtual:真实,
                标签名:'',
                _contextView:parentView,
                模板:模板,
                背景:得到(grandParentView,语境),//默认为得到(parentView,语境),
                控制器:得到(grandParentView,控制器),//默认为得到(parentView,语境),
                templateData:{关键字:parentView.cloneKeywords()}
            });
        }
    }
});

我还是不访问accordionPaneObject,但是现在我看到{{登录this.constructor}}输出.EmberAccordionComponent。所以会出现,我在正确的范围,但数据仍然未绑定。

有趣的是,如果我使用任何在我重写_yield重新分配情况和控制器的这些变化,我可以访问我用我在控制台后的数据:

  this._parentView._context.content


解决方案

我更新了code的一些意见,请给看看的 http://emberjs.jsbin.com/ivOyiZa/1/edit

的JavaScript

 应用= Ember.Application.create();App.IndexRoute = Ember.Route.extend({
  型号:功能(){
    返回[
      {头:富脑袋,身体:富体},
      {头:栏头,身体棒体},
      {头道:你的头,身体:哟身体}
    ];
  }
});App.EmberAccordionComponent = Ember.Component.extend({
  //每个手风琴头/体项目,将有视图的实例。
  //所以我们可以隔离展开状态的每个手风琴头/体
  emberAccordionItemView:Ember.View.extend({
    扩展:假的
  }),
  _yield:功能(背景下,期权){
    VAR GET = Ember.get,
    鉴于= options.data.view,
    parentView = this._parentView,
    模板=得到(这一点,'模板');    如果(模板){
      Ember.assert(A组分必须以得到具有父视图,parentView);
      view.appendChild(Ember.View,{
        isVirtual:真实,
        标签名:'',
        _contextView:parentView,
        模板:模板,
        背景:获得(查看,语境),//默认为得到(parentView,语境),
        控制器:得到(查看,控制器),//默认为得到(parentView,语境),
        templateData:{关键字:parentView.cloneKeywords()}
      });
    }
  }
});App.EmberAccordionHeaderComponent = Ember.Component.extend({
  类名:['烬手风琴头'],
  点击:函数(){
    //这里我们切换emberAccordionItemView.expanded财产
    this.toggleProperty('parentView.expanded');
  }
});

模板

 &LT;脚本类型=文/ X-车把数据模板的名称=指数&GT;
    {{#烬,手风琴listOfAccordionPaneObjects =模型}}
          {{#烬手风琴头}}
              {{头}}&LT;! - 在listOfAccordionPaneObjects传递的每个对象=模型可以访问这里 - &GT;
          {{/烬-手风琴头}}
          {{#烬手风琴体}}
              {{身体}}&LT;! - 在listOfAccordionPaneObjects传递的每个对象=模型可以访问这里 - &GT;
          {{/烬-手风琴体}}
    {{/烬,手风琴}}
  &LT; / SCRIPT&GT;  &LT;脚本类型=文/ X-车把数据模板的名称=组件/烬-手风琴&GT;
    {{#each listOfAccordionPaneObjects itemViewClass =view.emberAccordionItemView}}
      &LT; D​​IV CLASS =烬-手风琴容器&GT;
        &LT; D​​IV CLASS =烬-手风琴窗格&GT;
            {{产量}}
        &LT; / DIV&GT;
      &LT; / DIV&GT;
    {{/每}}
  &LT; / SCRIPT&GT;  &LT;脚本类型=文/ X-车把数据模板的名称=组件/烬-手风琴头&GT;
    {{产量}}
  &LT; / SCRIPT&GT;  &LT;脚本类型=文/ X-车把数据模板的名称=组件/烬-手风琴体&GT;
    &所述;! - 当EmberAccordionHeaderComponent.click被调用,膨胀属性的变化和该内容可以基于扩展真理是可见或不可见, - &GT;
    {{#如果parentView.expanded}}
      &LT; D​​IV CLASS =烬-手风琴窗格内容&GT;
        {{产量}}
      &LT; / DIV&GT;
    {{/如果}}
  &LT; / SCRIPT&GT;

的CSS

  .ember手风琴头{
  背景颜色:#999;
  颜色:#FFFFFF;
  填充:10px的;
  保证金:0;
  行高:14px的;
  -webkit-边框左上角的半径:5像素;
  -webkit-边框右上角半径:5像素;
  -moz-边界半径-左上:5像素;
  -moz-边界半径-topright:5像素;
  边框左上角的半径:5像素;
  边框右上角半径:5像素;
  光标:指针;
  文字修饰:无;
  FONT-FAMILY:Helvetica Neue字体,黑体,宋体,无衬线;
  字体大小:14px的;
}.ember手风琴容器{
  高度:100%;
  宽度:100%;
}.ember手风琴窗格{
  填充:2px的;
}.emberaccordionheaderselected {
  背景色:#bbb;
  颜色:#333;
  字体重量:大胆的;
}.ember-手风琴头:悬停{
  文字修饰:强调重要的;!
}.emberaccordionheaderselected:悬停{
  文字修饰:强调重要的;!
}.ember手风琴窗格内容{
  填充:5像素;
  溢出-Y:汽车;
  左边框:1px的固体#bbb;
  右边框:1px的固体#bbb;
  下边框:1px的固体#bbb;
  -webkit-边框左下角半径:5像素;
  -webkit-边框右下半径:5像素;
  -moz-边界半径-BOTTOMLEFT:5像素;
  -moz-边界半径-bottomright:5像素;
  边框左下角半径:5像素;
  边框右下角半径:5像素;
}.emberdisabledpane {
  不透明度:0.2;
}

I'm curious if this is even possible in ember. This is an easy thing to do in angular ( plunkr: http://plnkr.co/edit/O2e0ukyXdKMs4FcgKGmX?p=preview ):

The goal is to make an easy to use, generic, reusable accordion api for api consumers.

The api I want the caller to be able to use is this (just like the angular 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}}

Here is a working example I wrote using 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>

here's where I'm stuck doing the same with ember:

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}}

--

This is tricky to debug. So putting in the:

{{log this.constructor}}

and the:

{{log this}}

into the:

{{#ember-accordion-header}}

outputs the following:

  • Class.model = undefined (why?)
  • Ember.ArrayController

I've tried overriding the private _yield method of Ember.Component as suggested by this article ( http://www.thesoftwaresimpleton.com/blog/2013/11/21/component-block/ ):

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() }
            });
        }
    }
});

but when I do this I still don't have access to accordionPaneObject in my child component scope, and my {{log this.constructor}} now points to: .EmberAccordionHeaderComponent

So it looks like I'm getting somewhere, I just need to go one more level up.

When I try that using this code in 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() }
            });
        }
    }
});

I still don't access to accordionPaneObject in, but now I see {{log this.constructor}} outputting .EmberAccordionComponent. So it appears I'm in the right scope, but the data still doesn't bind.

Interestingly enough, if I use any of these variations of reassigning context and controller in my overridden _yield, I can access the data I am after in the console using:

this._parentView._context.content

解决方案

I updated your code with some comments please give a look http://emberjs.jsbin.com/ivOyiZa/1/edit.

Javascript

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');  
  }
});

Templates

  <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;
}

这篇关于如何从一个灰烬子组件范围内访问父组件的范围?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆