Knockout: コンポーネントの要素へのアクセスを取得します

作成日 2014年08月13日  ·  6コメント  ·  ソース: knockout/knockout

こんにちは!
新しい(そして優れた)ノックアウトコンポーネントを使用していて、コンポーネントのDOM要素にアクセスする必要がある状況に遭遇しました。 ただし、要素はcreateViewModelメソッドを使用する場合にのみ渡されるようであり、 viewModelがメソッドである場合は渡されません。 私はこれが何をしているのか疑問に思っていましたか? viewModelcreateViewModelの使用の違いがよくわかりませんが、 viewModelcreateViewModelの省略形ですか? その場合、DOM要素を取得する必要があるようです。

最も参考になるコメント

親要素へのアクセスが望ましい、私が現在解決しようとしている非常に優れたユースケースの1つは、次のとおりです。

私はツールバーを持っています、そしてこのツールバーはそれに多くの異なるコンポーネントを注入することができます(疑似コード)

<toolbar>
  <icon></icon>
  <icon></icon>
  <singlespace></singlespace>
  <menutab></menutab>
  <menutab></menutab>
  <menutab></menutab>
  <spaceconsumer></spaceconsumer>
  <icon></icon>
  <icon></icon>
</toolbar>

私の特定のケースでは、ツールバーのマークアップ内に次のものがあります。

<div class="menubarContainer" data-bind="foreach: menubarComponents">
  <div data-bind='component: { name: controlType , params: $data}'></div>
</div>

ご覧のとおり、これは非常に標準的なループバインディングであり、コンポーネントごとに1つのdivが生成され、「menubarComponents」の監視可能な配列で定義されます。

現状では、それは機能し、一般的にはうまく機能します。

しかし、私(そして他の多くの人もそうだと思います)は、最近、新しい「フレキシブルボックス」モデルをずっと使用しています。

Flexboxは、の複雑なレイアウトを大幅に簡素化します。たとえば、ツールバーに次のスタイルを追加できます。

.menubarContainer{
  display: flex;
}


次に、そのdiv内に含まれるすべてのdivを、フレックスアイテムの制御下に自動的に配置します。

繰り返しますが、私の特定のケースでは、私は次のことをしようとしています

<menubar style="display: flex;">
  <icon></icon>
  <icon></icon>
  <singlespace style="flex: 0 0 40px;"></singlespace>
  <menutab></menutab>
  <menutab></menutab>
  <menutab></menutab>
  <spaceconsumer style="flex: 1;"></spaceconsumer>
  <icon></icon>
  <icon></icon>
</menubar>

今回はいくつかの要素のスタイルに注意してください。

単一のスペースコンポーネントは、使用可能なスペースの正確に40ピクセルを占有し、アイコンとメニュータブの間に40ピクセルのスペースを作成します。一方、「スペースコンシューマー」は、その後の残りの40ピクセルと他のすべてのサイズを使用します。コンポーネントが差し引かれています。

結果として、非常によくレイアウトされたツールバーが作成されます。このツールバーには、左側に2つのアイコン、40ピクセルのスペース、3つのテキストベースのタブがあり、バーの右側に2つのアイコンがあり、それらと左の要素。

繰り返しになりますが、これはすべてうまく機能しますが、残念ながら、ここに尻尾の刺し傷があります。

親要素で「display:flex」を定義すると、flex子レイアウトは直接の子にのみ適用されます。これは、この投稿の場合、コンポーネントを含むデータバインドを持つdivを意味します。 (ディスプレイフレックスは、for-eachを持つdivにあります)

挿入するコンポーネントからフレックスボックスのサイズと間隔を制御したいので(コンポーネントの配列を定義することで再利用可能なメニューバーを構成できるという考えです。ほとんどのリクレイはjsonを介して読み込まれるか、パラメーターで渡されます)、理想的には、言うことができる方法が必要です。これが私の要素です。このcssスタイルルールをコンテナに追加してください。

フレックスコンテナにIDがあり、親で「#id> div.blah」を使用できる場合もありますが、強調表示した場合は、コンポーネントdivに追加のルールを適用することはできません。これは、ツールバーに挿入される可能性のあるすべての下位レベルのコンポーネントに適用されるためです。

したがって、要約すると、親要素を取得する方法がない場合でも(そして、その理由を理解している場合でも)、コンポーネントによって少なくともいくつかのcssを適用できるメカニズムを提供することは少なくともアイデアではありません。それにルール。

ショーティ

全てのコメント6件

ドキュメントがこれについて明確であることを確認したい場合がありますが、それは間違いなく設計どおりです。

理由は、ほとんどの場合、人々はビューモデルから直接要素に干渉するべきではないということです(MVVM全体を損なう)-これが代わりにバインディングの目的です! それは意図的に抽象化の追加レイヤーの背後にありますが、本当に必要な場合はcreateViewModelを介して可能です:)

良い点ですが、バインディングを呼び出す大きなコンポーネントを作成するだけでなく、DOMのものをラップするカスタム要素/コンポーネントを作成することがよくあると思います。 一部のコンポーネントには独自のビューモデルすらなく、テンプレートとDOMミューテーションのラッパーとしてのみ機能すると思います。

これも必要でした。 私は新しいコンポーネント機能が大好きで、それを使用して中規模/大規模SPAのさまざまなセクションをロードしたいと考えていました。 これまでに機能しているように見える回避策を見つけました。

説明:ページのすべてのセクションをコンポーネントモデルに適合させることはできますが、すべてのセクションがノックアウトを使用したいとは限りませんでした。 一部はすでにノックアウトでコーディングされており、applyBindingsを内部で呼び出すなどですが、他のセクションではノックアウトをまったく使用する必要はありません。 これらの後者のセクションには、すでに別のデータバインディングソリューションがあるか、非常に具体的なことを行っており、データバインディングを必要としません。 これらのコンポーネントは、レンダリングと一般的なdom要素を完全に制御する必要があります。 そのため、Backboneやthis。$のようなdom要素にアクセスする必要があります。ポリマーで(私が正しく理解している場合。

今のところ、要素をビューモデルのコンストラクターに挿入するローダーをつなぎ合わせました。 KOの将来のリリースに関して脆弱すぎないことを願っています。

ko.components.loaders.unshift({
    // Could this be accomplished cleaner with loadComponent?
    loadViewModel: function (name, viewModelConfig, callback) {
        if (typeof viewModelConfig === "function") {
            var viewModelConstructor = {
                createViewModel: function (params, componentInfo) {
                    return new viewModelConfig(params, componentInfo.element)
                }
            };
            ko.components.defaultLoader.loadViewModel(name, viewModelConstructor, callback);
        } else {
            callback(null);
        }
    }
});

また、典型的な「stopBinding」バインディングを作成しました。

ko.bindingHandlers.stopBinding = {
    init: function () {
        return { controlsDescendantBindings: true };
    }
};

ロードしたいが、実際には内部でKOを使用したくないコンポーネント。 完全な制御が必要

define(function (require) {

    var wrapperTemplate = require('text!./comp-non-ko-wrapper.html'),
        template = require('text!./comp-non-ko.html'),
        $ = require('jquery')
        _ = require('underscore');

    var ViewModel = function (params, element) {
        this.$el= $(element).find('.wrapper');
        this.template = _.template(template);
        this.render();
    }

    ViewModel.prototype.render = function () {
        // Render my custom, non ko template
        this.$el.html(this.template());
    }

    return {
        viewModel: ViewModel,
        template: wrapperTemplate
    };
});

私のコンポーネント/ラッパーテンプレート(stopBindingに注意してください):
comp-non-ko-wrapper.html

<div data-bind='stopBinding' class='wrapper'>
</div>

私の実際のテンプレート(データバインドは意図的に無視されます、それがテストです)
comp-non-ko.html

<!-- (Just making sure these bindings aren't picked up) -->
<div>Hello <span data-bind="text:name"></span>
</div>

このスレッドは古いことは知っていますが、デザインに関して指摘したいのは、Knockoutコンポーネントが、ビューモデルのプロパティから単純に(または効率的に)制御できない非常に複雑なビジュアルを必要とする場合があるということです。また、カスタムバインディングはそのコンポーネントの外部では使用されないため、グローバルスコープ内に含めることは意味がありません。 適切な例は、 d3.jsのSVGビジュアライゼーションのいずれかを使用することです。 カスタムバインディングのコンポーネントレベルの定義を許可すれば、MVVMデザインパターンを壊すことなくこの問題を解決できる可能性があります。

親要素へのアクセスが望ましい、私が現在解決しようとしている非常に優れたユースケースの1つは、次のとおりです。

私はツールバーを持っています、そしてこのツールバーはそれに多くの異なるコンポーネントを注入することができます(疑似コード)

<toolbar>
  <icon></icon>
  <icon></icon>
  <singlespace></singlespace>
  <menutab></menutab>
  <menutab></menutab>
  <menutab></menutab>
  <spaceconsumer></spaceconsumer>
  <icon></icon>
  <icon></icon>
</toolbar>

私の特定のケースでは、ツールバーのマークアップ内に次のものがあります。

<div class="menubarContainer" data-bind="foreach: menubarComponents">
  <div data-bind='component: { name: controlType , params: $data}'></div>
</div>

ご覧のとおり、これは非常に標準的なループバインディングであり、コンポーネントごとに1つのdivが生成され、「menubarComponents」の監視可能な配列で定義されます。

現状では、それは機能し、一般的にはうまく機能します。

しかし、私(そして他の多くの人もそうだと思います)は、最近、新しい「フレキシブルボックス」モデルをずっと使用しています。

Flexboxは、の複雑なレイアウトを大幅に簡素化します。たとえば、ツールバーに次のスタイルを追加できます。

.menubarContainer{
  display: flex;
}


次に、そのdiv内に含まれるすべてのdivを、フレックスアイテムの制御下に自動的に配置します。

繰り返しますが、私の特定のケースでは、私は次のことをしようとしています

<menubar style="display: flex;">
  <icon></icon>
  <icon></icon>
  <singlespace style="flex: 0 0 40px;"></singlespace>
  <menutab></menutab>
  <menutab></menutab>
  <menutab></menutab>
  <spaceconsumer style="flex: 1;"></spaceconsumer>
  <icon></icon>
  <icon></icon>
</menubar>

今回はいくつかの要素のスタイルに注意してください。

単一のスペースコンポーネントは、使用可能なスペースの正確に40ピクセルを占有し、アイコンとメニュータブの間に40ピクセルのスペースを作成します。一方、「スペースコンシューマー」は、その後の残りの40ピクセルと他のすべてのサイズを使用します。コンポーネントが差し引かれています。

結果として、非常によくレイアウトされたツールバーが作成されます。このツールバーには、左側に2つのアイコン、40ピクセルのスペース、3つのテキストベースのタブがあり、バーの右側に2つのアイコンがあり、それらと左の要素。

繰り返しになりますが、これはすべてうまく機能しますが、残念ながら、ここに尻尾の刺し傷があります。

親要素で「display:flex」を定義すると、flex子レイアウトは直接の子にのみ適用されます。これは、この投稿の場合、コンポーネントを含むデータバインドを持つdivを意味します。 (ディスプレイフレックスは、for-eachを持つdivにあります)

挿入するコンポーネントからフレックスボックスのサイズと間隔を制御したいので(コンポーネントの配列を定義することで再利用可能なメニューバーを構成できるという考えです。ほとんどのリクレイはjsonを介して読み込まれるか、パラメーターで渡されます)、理想的には、言うことができる方法が必要です。これが私の要素です。このcssスタイルルールをコンテナに追加してください。

フレックスコンテナにIDがあり、親で「#id> div.blah」を使用できる場合もありますが、強調表示した場合は、コンポーネントdivに追加のルールを適用することはできません。これは、ツールバーに挿入される可能性のあるすべての下位レベルのコンポーネントに適用されるためです。

したがって、要約すると、親要素を取得する方法がない場合でも(そして、その理由を理解している場合でも)、コンポーネントによって少なくともいくつかのcssを適用できるメカニズムを提供することは少なくともアイデアではありません。それにルール。

ショーティ

このページは役に立ちましたか?
0 / 5 - 0 評価