Pregunta

The Anchor tag is used here like a button and the knockout click binding is associated to a function on the view model. Why it is requiring two clicks for the function to be invoked?

<script type="text/javascript" src="http://ajax.googleapis.com/ajax/libs/jquery/1.11.0/jquery.min.js">
</script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/knockout/3.1.0/knockout-min.js">
</script>
<script type="text/javascript" src="http://cdnjs.cloudflare.com/ajax/libs/knockout.mapping/2.4.1/knockout.mapping.js">
</script>
<script type="text/javascript">
    var PositionModel = function (data) {
        var self = this;

        self.listPosition = ko.mapping.fromJS(data);
        if (self.listPosition == undefined) {
            self.listPosition = ko.observableArray();
        }
        self.selectedItemPosition = ko.observable();
        self.positionToEdit = ko.observable();

        self.addPosition = function () {
            var newItem = ko.mapping.fromJS({
                Id: 0,
                Name: ''
            });
            self.listPosition.splice(0, 0, newItem);
            self.listPosition.valueHasMutated();
            self.positionToEdit(newItem);
            $('#txtPositionName').focus();
        };

        self.savePosition = function () {
            var item = self.positionToEdit();

            self.positionToEdit(null);
        };

        self.selectPosition = function (item) {
            ////////debugger;
            self.selectedItemPosition(item);
        }

        self.templateToUsePosition = function (item) {
            console.log(item.Name());
            var compare = self.positionToEdit();
            var str = compare === item ? 'editTmplPosition' : 'itemsTmplPosition';
            return str;
        };

        self.editPosition = function (item) {
            self.positionToEdit(item);
        };

    };

    $(function () {

        ko.applyBindings(new PositionModel([{
            Id: 1,
            Name: 'abc'
        }, { Id: 2, Name: 'def' }]));

    });
</script>
<div id="abcd">
    <div>

        <table  style="width: 400px">
            <p>
                <a  data-bind="click: addPosition" href="#" title="edit">Add</a>
            </p>

            <thead>
                <tr>
                    <th>
                        <a href="#" >position</a>
                    </th>
                    <th style="width: 100px; text-align: right;" />
                </tr>
            </thead>
            <tbody data-bind="template: { name: templateToUsePosition, foreach: listPosition }" />
        </table>

        <script id="itemsTmplPosition" type="text/html">
            <tr style="width: auto" data-bind="click: $parent.selectPosition">
                <td style="width: auto" data-bind="text: Name" />
                <td  style="width: 200px">
                    <a  data-bind="click: $parent.editPosition" href="#" title="edit">
                        edit
                    </a>
                    <a  data-bind="click: $parent.removePosition" href="#" title="remove">
                        remove
                    </a>
                </td>
            </tr>
        </script>
        <script id="editTmplPosition" type="text/html">
            <tr>
                <td>
                    <input style="width: auto" id="txtPositionName" data-bind="value: Name" />
                </td>
                <td >
                    <a  data-bind="click: $parent.savePosition" href="javascript:void(0);" title="save">
                        save
                    </a>
                    <a  data-bind="click: $parent.cancelSavingPosition" href="#" title="cancel">
                        cancel
                    </a>
                </td>
            </tr>
        </script>

    </div>
</div>
¿Fue útil?

Solución

With writing console.log(item.Name()); into your templateToUsePosition function you have created a dependency on the Name property in the KO dependency tracking mechanism.

So every time when your item.Name() changes your templateToUsePosition will be reevaluated.

So this is what happens in your code:

  • You click on edit which changes positionToEdit which triggers your templateToUsePosition function and turn your UI to edit mode

  • You enter something in the textbox and click on the save link

  • Because the textbox loses the focus it updates your Name property

  • Because the templateToUsePosition has a dependency on the Name property it is triggered again and rebuilds your UI, so your original click event on the original save link is lost and you have to click again.

  • One the second click there is no change in the Name so it the click even normally goes through.

So to solve this problem you just need to remove the console.log(item.Name());.

However if you still need to use the value of item.Name in your templateToUsePosition and you don't want to create a dependency then you need to use the peek() method on the observable:

console.log(item.Name.peek());

Demo JSFiddle.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top