Problem: I'm trying to build a dashboard of widgets. Each widget will have a delete button on its header. When clicked on this button, corresponding widget have to disappear.
How I designed: I have two knockout components.
Note: For simplicity, I'm replacing the widget object with just numbers.
ko.components.register('my-widget-list', {
viewModel : function(params) {
var self = this;
self.values = ko.observableArray([10,20,30,40,50]);
self.deleteWidget = function(obj)
{
self.values.remove(obj);
}
},
template: {element: 'my-widget-list-template'}
});
ko.components.register('my-widget', {
viewModel : function(params) {
var self = this;
self.value = params.value;
},
template: {element: 'my-widget-template'}
});
ko.applyBindings({});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<my-widget-list></my-widget-list>
<script id="my-widget-list-template" type="text/html">
<div data-bind="foreach:values">
<my-widget params="value: $data"></my-widget><br>
</div>
</script>
<script id="my-widget-template" type="text/html">
<span data-bind="text: value"></span>
<button data-bind="click: $parent.deleteWidget">Delete</button>
</script>
Now, I want to invoke my-widget-list
's deleteWidget
function when the button is clicked.
I have thought about
But I wish to know from experts what's the best way to achieve this.
Thanks in advance
You can pass in the parent as a param to the child:
ko.components.register('my-widget-list', {
viewModel : function(params) {
var self = this;
self.values = ko.observableArray([10,20,30,40,50]);
self.deleteWidget = function(obj) {
self.values.remove(obj);
}
},
template: {element: 'my-widget-list-template'}
});
ko.components.register('my-widget', {
viewModel : function(params) {
var self = this;
self.value = params.value;
self.remove = function () {
params.parent.deleteWidget(self.value);
};
},
template: {element: 'my-widget-template'}
});
ko.applyBindings({});
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<my-widget-list></my-widget-list>
<script id="my-widget-list-template" type="text/html">
<div data-bind="foreach:values">
<my-widget params="value: $data, parent: $parent"></my-widget><br>
</div>
</script>
<script id="my-widget-template" type="text/html">
<span data-bind="text: value"></span>
<button data-bind="click: remove">Delete</button>
</script>
But I'm not sure if that is a good idea, as it needlessly couples the child to the parent.
I'd recommend implementing the "remove" button in the parent, i.e. in <my-widget-list>
, this way the widget can exist without a widget-list (or in a differently structured one) while the widget-list is in control of its children.
Compare window managers: They work the same way. The window manager draws the frame and the minimize/maximize/close buttons, while the window contents is drawn by the respective child process. That logic makes sense in your scenario as well.
Alternative implementation with removeWidget
control in the parent:
ko.components.register('my-widget-list', {
viewModel : function(params) {
var self = this;
self.values = ko.observableArray([10,20,30,40,50]);
self.deleteWidget = function(obj) {
self.values.remove(obj);
}
},
template: {element: 'my-widget-list-template'}
});
ko.components.register('my-widget', {
viewModel : function(params) {
var self = this;
self.value = params.value;
},
template: {element: 'my-widget-template'}
});
ko.applyBindings({});
.widget-container {
position: relative;
display: inline-block;
padding: 10px 5px 5px 5px;
margin: 0 5px 5px 0;
border: 1px solid silver;
border-radius: 2px;
min-width: 40px;
}
.widget-buttons {
position: absolute;
top: 2px;
right: 2px;
}
.widget-buttons > button {
font-size: 2px;
padding: 0;
height: 15px;
width: 15px;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<my-widget-list></my-widget-list>
<script id="my-widget-list-template" type="text/html">
<div class="widget-list" data-bind="foreach:values">
<div class="widget-container">
<div class="widget-buttons">
<button data-bind="click: $parent.deleteWidget">X</button>
</div>
<my-widget params="value: $data"></my-widget>
</div>
</div>
</script>
<script id="my-widget-template" type="text/html">
<div class="widget">
<span data-bind="text: value"></span>
</div>
</script>