How to load a KnockoutJS component programmaticall

2019-04-13 22:37发布

I'm starting to create a ko component for select2 called "select-two", but I realize soon after that I sometimes need to load components after bindings have been registered creating the component programmatically.

I've tried apending a new element but of course it doesnt work, I'm guessing it has to be rebinded.

var sel2 = $("<select-two></select-two>") ;
$("#selectList").append(sel2) ; 

There's lot of reference on how to rebind the whole viewmodel, but only just the component? I think this guy has the same problem: Load knockoutjs component using javascript

Just for reference this is the component code:

define([
'knockout',
'text!./select-two.html',
'select2'
], function (ko,  templateMarkup, select2) {
ko.components.register('select-two', {
    viewModel: function(params) {

        var placeholder = params.placeholder;
        var value = params.value;

        ko.bindingHandlers.select2 = {
            init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
                $(element).select2({
                        placeholder: placeholder,
                        minimumInputLength: 1,
                        ajax: {
                            url: function (term, page) {
                                return '/models/autores/busqueda/' + term['term']
                            },
                            dataType: 'json',
                            quietMillis: 200,
                            processResults: function (data) {
                                return data;
                            }
                        },
                        dropdownCssClass: "bigdrop", // apply css that makes the dropdown taller
                    }
                ).on('change', function(event){
                        ds = $(element).select2('data')[0] ;

                        value['id'](ds['id']) ;
                        value['text'](ds['text']) ;
                    });
            }
        };
        return{
        }
    },
    template: templateMarkup
});
});

my template:

<link rel="stylesheet" href="/assets/js/vendor/select2/dist/css/select2.css" />
<select class="form-control" data-bind="select2"></select>

and how I load it:

<select-two id="authorSelect" params="placeholder: 'Pick an Author', value: autorSelectData" ></select-two>

ko.bindingHandlers.select2 = {
   init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
      var params = ko.unwrap(valueAccessor());
      $(element).select2({
         placeholder: params.placeholder,
         minimumInputLength: 1,
         data: params.data
       }).on('select2:select', function(e) { 
        params.value(e.params.data.text);
       });
      $(element).select2('val', params.value); // set initial value
   }
};

ko.components.register('select-two', {
  viewModel: function(params) {
    this.value = params.value;
    this.data = params.data;
  },
  template: '<select class="form-control" style="width: 200px;" data-bind="select2: ' + 
            '{placeholder: \'Pick a fruit\', value: value, data: data}">' +
            '</select>'
});

var app = {
  newSelect: function(){
    var cont = $("<p></p>") ; 
    var sel2 = $("<select-two></select-two>") ; 
    ko.applyBindings({}, sel2[0]) ;
    cont.append(sel2) ; 
    $("#select-list").append(cont) ; 
    
  },
  autorSelectData: ko.observable(null), 
  options: ['apple','pear','peach','mango','grape']
};

ko.applyBindings(app);
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
Just the binding: <select class="form-control" style="width: 200px;"
        data-bind="select2: {placeholder: 'Pick a fruit', value: autorSelectData, data: options}">
</select><br>
With component: <select-two params="value: autorSelectData, data: options"></select-two>

<p>Adding Selects</p>
<div id="select-list">
  
</div>

<input type="button" data-bind="click: newSelect()" value="New Select" id="new-select" />

1条回答
劫难
2楼-- · 2019-04-13 23:16

I'm guessing it has to be rebinded.

Please remember that when you think of rebinding as a solution, in 99% of the cases you should reconsider your approach.

creating the component programmatically.

If you follow MVVM/ MVC guidelines, you should never have to do this. All data models/ views' structures are pre-defined; only their content can change.

What shouldn't you be doing?

  • Registering a custom binding inside a component, which means it will be re-registered every time a component instance is created.
  • Linking a style sheet inside a component template, which means the stylesheet will be loaded every time a component instance is created (instead of once).
  • Using the change event while the select2 docs show you have to use select2:select.

Below is a stripped down version of your code (eg., data-array instead of AJAX), showing how it works with (1) just the binding, and (2) with an encapsulatin component. Does it still not work, and if so, what does not work?

ko.bindingHandlers.select2 = {
   init: function (element, valueAccessor, allBindings, viewModel, bindingContext) {
      var params = ko.unwrap(valueAccessor());
      $(element).select2({
         placeholder: params.placeholder,
         minimumInputLength: 1,
         data: params.data
       }).on('select2:select', function(e) { 
        params.value(e.params.data.text);
       });
      $(element).select2('val', params.value); // set initial value
   }
};

ko.components.register('select-two', {
  viewModel: function(params) {
    this.value = params.value;
    this.data = params.data;
  },
  template: '<select class="form-control" style="width: 200px;" data-bind="select2: ' + 
            '{placeholder: \'Pick a fruit\', value: value, data: data}">' +
            '</select>'
});

var app = {
  autorSelectData: ko.observable(null), 
  options: ['apple','pear','peach','mango','grape']
};

ko.applyBindings(app);
<link href="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/css/select2.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/select2/4.0.0/js/select2.min.js"></script>
Just the binding: <select class="form-control" style="width: 200px;"
        data-bind="select2: {placeholder: 'Pick a fruit', value: autorSelectData, data: options}">
</select><br>
With component: <select-two params="value: autorSelectData, data: options"></select-two>

查看更多
登录 后发表回答