Refactoring ViewModel with multiple collection properties

5 posts, 0 answers
  1. Pavel
    Pavel avatar
    15 posts
    Member since:
    May 2012

    Posted 05 Jan 2015 Link to this post

    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.
  2. Pavel
    Pavel avatar
    15 posts
    Member since:
    May 2012

    Posted 05 Jan 2015 in reply to Pavel Link to this post

    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>

  3. Kendo UI is VS 2017 Ready
  4. Petyo
    Admin
    Petyo avatar
    2439 posts

    Posted 07 Jan 2015 Link to this post

    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!
     
  5. Pavel
    Pavel avatar
    15 posts
    Member since:
    May 2012

    Posted 07 Jan 2015 in reply to Petyo Link to this post

    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?
  6. Petyo
    Admin
    Petyo avatar
    2439 posts

    Posted 09 Jan 2015 Link to this post

    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!
     
Back to Top
Kendo UI is VS 2017 Ready