0
votes

So right now I have a table that displays some values and I have an indicator for conflicts. When the user clicks the indicator a new div appears with some animation to list all the conflicts.

Here is my HTML:

<span data-bind="if: hasConflict, click: $parent.selectProperty" class="conflictWarn"><i style="color: darkorange; cursor:pointer;" class="icon-warning-sign"></i></span>

The data might look something like this:

{
 name:Property 1,
 id: 1,
 hasConflicts: no,
 name:Property 2,
 id: 2,
 hasConflicts: yes,
 conflicts: {
             name: conflict1,
             name: conflict2
            }
 name:Property 3,
 id: 3,
 hasConflicts: yes,
 conflicts: {
             name: conflicta,
             name: conflictb
            }

So the first table is going to look like this:

Property 1
Property 2   !
Property 3   !

Where ! is a conflict indicator. Clicking on the ! would display the conflicts div and also display conflict1 and conflict2 or conflicta and conflictb depending on which was clicked.

Here is the model we are working with. It's a bit complex because of the mapping for the properties from signalr. the "selectProperty" and "selectedProperty" was our way of saying which one to display conflicts for, but I'm not convinced this is the best way to do it.

function ItemViewModel() {
    var self = this;
    self.name = ko.observable("");
    self.itemType = ko.observable("");
    self.propertiesArray = ko.observableArray([]);
    self.properties = ko.mapping.fromJS({});
    self.selectedPropertyName = ko.observable("");
    self.getItem = function (name) {
        $.connection.mainHub.server.getItem(name).then(function (item) {
            ko.mapping.fromJS(item.properties, self.properties);
            self.propertiesArray(item.propertiesArray);
            self.itemType(item.itemType.name);
            self.name(item.name);
        });
        self.selectProperty = function (a, b) {
            self.selectedPropertyName(a);
        };
    };

}

Originally the click event directly called a javascript function that did all the animation, but my coworker thought that might violate best practices for separating data and viewmodel in MVVM. Does it? Should we leave it calling the viewmodel function of "selectProperty" which allows us to pass context for the "conflicts popup" div? If so, do I just call the javascript function to do the animation from within the selectProperty function?

p.s. I've edited this about 800 times so I apologize if it's impossible to follow.

update I have the bindings working now, so I really just want to know what is best practice when it comes to UI animations and Knockout. Change the viewmodel from the javascript function or call the javascript function from the viewmodel function?

2
Tomas's answer is the best practice, when in doubt do it the way it's described on the framework website. added bonus, knockout is exceptionally documented - hubson bropa

2 Answers

1
votes

Regarding UI animations in my opinion it is best practice to implement custom bindings. This way code is encapsulated and it is easy to find where it is used. Check Animated transitions example on knockout website.

0
votes

i'm going to extends Thomas answer with one point, custom bindings don't work when you want to animate the rendering / unrendering of the 'if' or 'with' bindings. an animation binding that tries to run at the same time as an 'if' or 'with' won't be able to complete the animation before the other binding alters the DOM, possibly removing the elements being animated from the page. there is no way to defer the binding processing until after the event completes.

for these cases animations should be implemented via the 'afterAdd' and 'beforeRemove' callbacks of the 'foreach' binding when the desire is to animate an element being added and removed from the page. 'if' and 'with' bindings can be rewritten as 'foreach' with little effort, as 'foreach' can take a single argument instead of a list. i really wish the animation tutorial would be extended to include this workaround.