This is a migrated thread and some comments may be shown as answers.

Refactoring ViewModel with multiple collection properties

4 Answers 110 Views
MVVM
This is a migrated thread and some comments may be shown as answers.
Pavel
Top achievements
Rank 1
Pavel asked on 05 Jan 2015, 01:06 PM
Hi all,
I have a view model with 2 array properties: "prices" and "pricesForStudents" (objects inside arrays are of the same structure):

var model = kendo.observable({<br>  prices: [<br>            { hours: 24, firstYear: 7500, advanced: 8200 }, { ... }<br>        ],<br>        pricesForStudents: [<br>            { hours: 24, firstYear: 6000, advanced: 6300 }, { ... }<br>        ],<br><br>        ... other properties<br>});


I bind these 2 properties to 2 tables using templates, and I also have "Add" and "Remove" buttons.
I need to ensure each table has at least one row (each array has at least one element), so "Remove" button gets disabled, if the appropriate table contains only one row.
So, I introduced the following 3 methods in the ViewModel:

addPrice: function() {<br>    this.get("prices").push({});<br>},<br>removePrice: function(e) {<br>    this.get("prices").remove(e.data);<br>}<br>removePriceButtonEnabled: function() {<br>    return this.get("prices").length > 1;<br>}


But since I have 2 arrays I need to repeat the same functions for the second array:

addPriceForStudents: function() {<br>    this.get("pricesForStudents").push({});<br>},<br>removePriceForStudents: function(e) {<br>    this.get("pricesForStudents").remove(e.data);<br>}<br>removePriceForStudentsButtonEnabled: function() {<br>    return this.get("pricesForStudents").length > 1;<br>}


Both tables share the same template to display data:

<script id="regularPricing" type="text/x-kendo-template"><br>    <tr><br>        <td><input type="number" class="numerid-box single-line" data-bind="value: hours" data-role="numerictextbox" data-format="n0" /></td><br>        <td><input type="number" class="numerid-box single-line" data-bind="value: firstYear" data-role="numerictextbox" data-decimals="2" /></td><br>        <td><input type="number" class="numerid-box single-line" data-bind="value: advanced" data-role="numerictextbox" data-decimals="2" /></td><br>        <td><button data-bind="enabled: removePriceButtonEnabled, click: removePrice" data-role="button">Remove</button></td><br>    </tr><br></script>


The only detail here is that "Remove" button must be bound to different properties for the second table, so I repeated the template:

<script id="regularPricing" type="text/x-kendo-template"><br>    <tr><br>        <td><input type="number" class="numerid-box single-line" data-bind="value: hours" data-role="numerictextbox" data-format="n0" /></td><br>        <td><input type="number" class="numerid-box single-line" data-bind="value: firstYear" data-role="numerictextbox" data-decimals="2" /></td><br>        <td><input type="number" class="numerid-box single-line" data-bind="value: advanced" data-role="numerictextbox" data-decimals="2" /></td><br>        <td><button data-bind="enabled: removePriceForStudentsButtonEnabled, click: removePriceForStudents" data-role="button">Remove</button></td><br>    </tr><br></script>


It works, but there's a lot of repeating code (and even templates).
Later I will probably have 3 arrays insted of 2 now. 
Since Kendo does not allow javascript in data-bind attributes (like knockout js), seems like I have to define a separate property or method in the view model for each case, even if it is logically repeating another property/method.
Can it be refactored somehow?

Thanks.

4 Answers, 1 is accepted

Sort by
0
Pavel
Top achievements
Rank 1
answered on 05 Jan 2015, 01:16 PM
Formatting got broken in the initial post, so I post the code once more:

The model:
var model = kendo.observable({
    prices: [
        { hours: 24, firstYear: 7500, advanced: 8200 }, { ... }
    ],
    pricesForStudents: [
        { hours: 24, firstYear: 6000, advanced: 6300 }, { ... }
    ],
    ... other properties
});

Add/remove functions:

addPrice: function() {
    this.get("prices").push({});
},
removePrice:
function(e) {
    this.get("prices").remove(e.data);
}
removePriceButtonEnabled:
function() {
    return this.get("prices").length > 1;
}

Duplicated add/remove functions:

addPriceForStudents: function() {
    this.get("pricesForStudents").push({});
},
removePriceForStudents:
function(e) {
    this.get("pricesForStudents").remove(e.data);
}
removePriceForStudentsButtonEnabled:
function() {
    return this.get("pricesForStudents").length > 1;
}

Template:

<script id="prices" type="text/x-kendo-template">
    <tr>
        <td><input type="number" class="numerid-box single-line" data-bind="value: hours" data-role="numerictextbox" data-format="n0" /></td>
        <td><input type="number" class="numerid-box single-line" data-bind="value: firstYear" data-role="numerictextbox" data-decimals="2" /></td>
        <td><input type="number" class="numerid-box single-line" data-bind="value: advanced" data-role="numerictextbox" data-decimals="2" /></td>
        <td><button data-bind="enabled: removePriceButtonEnabled, click: removePrice" data-role="button">Remove</button></td>
    </tr>
</script>

Duplicated template:

<script id="pricesForStudents" type="text/x-kendo-template">
    <tr>
        <td><input type="number" class="numerid-box single-line" data-bind="value: hours" data-role="numerictextbox" data-format="n0" /></td>
        <td><input type="number" class="numerid-box single-line" data-bind="value: firstYear" data-role="numerictextbox" data-decimals="2" /></td>
        <td><input type="number" class="numerid-box single-line" data-bind="value: advanced" data-role="numerictextbox" data-decimals="2" /></td>
        <td><button data-bind="enabled: removePriceForStudentsButtonEnabled, click: removePriceForStudents" data-role="button">Remove</button></td>
    </tr>
</script>

0
Petyo
Telerik team
answered on 07 Jan 2015, 08:24 AM

Hello Pavel,

you can use custom classes with a common base one for both collections, and extract these methods there. After that, your binding would be something like price.removeButtonEnabled.

Regards,
Petyo
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
0
Pavel
Top achievements
Rank 1
answered on 07 Jan 2015, 12:00 PM
Hello Petyo,
Should I extend Kendo's ObservableArray class?
Could you please give a code sample how to extract repeating methods in a proper way?
0
Petyo
Telerik team
answered on 09 Jan 2015, 08:25 AM

Hello Pavel,

you don't need the observable array, unless you are going to do something on its change events. Plenty of examples are available in this help article, including methods in a base class. 

Regards,
Petyo
Telerik
 
Join us on our journey to create the world's most complete HTML 5 UI Framework - download Kendo UI now!
 
Tags
MVVM
Asked by
Pavel
Top achievements
Rank 1
Answers by
Pavel
Top achievements
Rank 1
Petyo
Telerik team
Share this question
or