Javascript和Knockout

时间:2013-12-03 00:51:41

标签: javascript asp.net-mvc binding knockout.js

我是Javascript和Knockout的新手。我坚持要绑定我的ViewModal。当ViewModal和View位于同一个Index.chtml文件中时,以下代码正在运行

ProfilesViewModel = function () {
    self = this;
    self.ProfileId = ko.observable("");
    self.FirstName = ko.observable("");
    self.LastName = ko.observable("");
    self.Email = ko.observable("");
    self.Image = ko.observable("");
    self.Phone = ko.observable("");

    // Public data properties
    self.Profiles = ko.observableArray([]);

    GetAllProfiles();

    function GetAllProfiles() {
        //  alert("here");
        $.ajax({
            type: "get",
            url: "@Url.Action("getAllProfiles")",
            contentType: "application/json; charset=utf-8",
            dataType: "json",
            success: function (data) {
               self.Profiles(data); //Put the response in ObservableArray
            },
            error: function (error) {
               alert(error.status + "<--and--> " + error.statusText);
            }
        });
    };
}

但是当我将ViewModal移动到另一个Js文件时,如下所示 Modal.js 代码:

ProfilesViewModel = function () {
    self = this;
    self.ProfileId = ko.observable("");
    self.FirstName = ko.observable("");
    self.LastName = ko.observable("");
    self.Email = ko.observable("");
    self.Image = ko.observable("");
    self.Phone = ko.observable("");

    // Public data properties
    self.Profiles = ko.observableArray([]);
};

ko.applyBindings(new ProfilesViewModel()); // This makes Knockout get to work

并在 Index.cshtml

var obj = new ProfilesViewModel();

GetAllProfiles();

function GetAllProfiles() {
    //  alert("here");
    $.ajax({
        type: "get",
        url: "@Url.Action("getAllProfiles")",
        contentType: "application/json; charset=utf-8",
        dataType: "json",
        success: function (data) {
            obj.Profiles(data); //Put the response in ObservableArray
        },
        error: function (error) {
            alert(error.status + "<--and--> " + error.statusText);
        }
    });
}

绑定不成功。 我不知道我在哪里做错了。请咨询。

如果建议更好的模块化方法,我也将不胜感激。

4 个答案:

答案 0 :(得分:5)

在您的外部JS中,您使用内联ko.applyBindings来调用ProfileViewModel,但稍后当您致电GetAllProfiles时,您正在使用另一个实例ProfileViewModel没有约束。

这里你的JS遇到了很多问题。既然你是新手,你需要知道的最重要的事情就是命名空间。在全局范围内定义变量和函数是危险的。您希望将自定义命名空间中的所有代码封装到应用程序中。这很简单:

var MyAwesomeNamespace = MyAwesomeNamespace || {};

随意调用它,但尝试选择您或您的应用程序独有的东西 - 其他人不太可能使用它。然后,一旦你有了,只需将事物定义为命名空间的属性:

MyAwesomeNamespace.SomeFormerlyGlobalVariable = 'foo';

然后,在处理像AJAX这样的事情时,最好创建一个实用程序/服务对象来保存该逻辑。如果它仅适用于此页面,则约定是在页面的主要对象或目的之后命名。根据你的情况,我会做类似的事情:

MyAwesomeNamespace.ProfileDisplay = function () {
    var _getAllProfiles = function () { ... };

    return {
        GetAllProfiles: _getAllProfiles
    }
})();

以上称为闭包。我们将ProfileDisplay设置为一个立即调用的匿名函数(最后是())。这将使ProfileDisplay的值成为函数的返回值,因此您现在可以拥有私有和公共成员,有点像更传统的面向对象类。函数未返回的任何内容都是私有的,代码的其余部分无法访问,而返回的对象中的项目是公共的,并且可用作其余代码可用的API。现在你可以通过以下方式调用你的AJAX:

MyAwesomeNamespace.ProfileDisplay.GetAllProfiles();

但是,我们现在再添加一个回调参数。无论您是否需要对此AJAX端点进行多次调用,都可以将其概括为一般,以便可以在不同场景中使用(如果需要)。 AJAX调用将始终是相同的,但成功时发生的是上下文,因此这是我们将抽象的部分。在这里,我也将切换到较不详细的$.getJSON,因为你在这里没有做任何疯狂的事情,实际上需要 $.ajax

MyAwesomeNamespace.ProfileDisplay = function () {
    var _getAllProfiles = function (callback) {
        $.getJSON('/api/profiles')
            .done(callback)
            .fail(function (jqXHR, textStatus, error) {
                alert(error.status + "<--and-->" + error.statusText);
            });
    };

    return {
        GetAllProfiles: _getAllProfiles
    }
})();

这很容易。现在,您有了一个可重用的方法来获取配置文件。我还投入了另一个名为“承诺”的功能。这是一个自己的话题,我鼓励你自己进一步调查。我只会说虽然这里使用非常简单,但它们非常强大。

最后,您在命名空间中以相同的方式定义视图模型。但是,这里我们将定义将处理AJAX响应的回调,因为viewModel本身就可以访问自身,这使得它很容易获得Knockout observable。

MyAwesomeNamespace.ProfileViewModel = function () {
    var self = this;

    self.UpdateProfileArray = function () {
        MyAwesomeNamespace.ProfileDisplay.GetAllProfiles(function (result) {
            self.Profiles(result);
        });
    });

    ...
}

最后,我们将使用初始化方法和连接页面事件的方法扩展我们的实用程序对象:

MyAwesomeNamespace.ProfileDisplay = function () {
    var _init = function () {
        var viewModel = MyAwesomeNamespace.ProfileViewModel();
        ko.applyBindings(viewModel);
        _wireEvents(viewModel);
        // the initial fetch of profiles list on page load
        viewModel.UpdateProfilesArray();
    });

    var _wireEvents = function (viewModel) {
        // Imagining a button that can be clicked to refresh list of
        // profiles on demand to illustrate how you're wire everything
        // together here.
        $('#RefreshProfilesButton').on('click', viewModel.UpdateProfilesArray);
    });

    ...

    // See, here we're not returning `_wireEvents` because public
    // access is not needed
    return {
        Init: _init,
        GetAllProfiles: _getAllProfiles
    }
});

所有代码都可以安全地放入外部JS中。最后,有了这个,您在实际视图中所需要的只是:

<script src="/path/to/external.js"></script>
<script>
    $(document).ready(function () {
        MyAwesomeNamespace.ProfileDisplay.Init();
    });
</script>

更干净。

最后关于AJAX方法的URL的一个词。在这里,我对其进行了硬编码,以免分散讨论的注意力,但最好依靠路由框架来为您提供信息。我们无法从外部javascript文件中访问它,这意味着我们必须通过内联javascript传递它。这是另一个拥有命名空间有助于保持秩序的领域。所以,首先我们要初始化我们的命名空间:

var MyAwesomeNamespace = MyAwesomeNamespace || {};

您希望首先将该行添加到您的所有JavaScript中。它确保如果命名空间尚未初始化,则将其初始化为空对象。 =与由||分隔的二进制值的组合,实质上意味着,如果此对象已设置,则将其设置为自身(基本上什么都不做),或者如果没有,则将其设置为空对象。

然后,我们可以在命名空间中设置一个变量来保存我们的URL。 (实际上,您可能希望进一步设置URLs对象或命名空间之外的东西,然后指定其中的URL变量,只是为了使事情变得更加整洁。)

MyAwesomeNamespace.GetAllProfilesURL = '@Url.Action("getAllProfiles")';

然后修改_getAllProfiles方法:

var _getAllProfiles = function (callback) {
    $.getJSON(MyAwesomeNamespace.GetAllProfilesURL)
        .done(callback)
        .fail(function () { ... });
}

答案 1 :(得分:0)

我认为这是因为你在加载dom之前调用了ko.applyBindings。如果你使用jQuery写如下:

$(document).ready(function() {
    ko.applyBindings(new ProfilesViewModel());
});

此外,您可以将脚本标记放在index.html的底部(在正文结束标记之前)。

答案 2 :(得分:0)

我倾向于为我的视图模型使用模块化方法...所以我会制作可重复使用的模型,并通过我的viewmodel中的合成为每个页面添加它们......例如:

function SomeModel()
{
   this.Id = ko.observable(0);
   this.Name = ko.observable("Some Default Name");
}

这可以通过var someModel = new SomeModel();来使用,这有助于您在页面中分离您的关注点,就像您有一个包含登录组件,用户详细信息组件以及可以表达您的产品组件的页面一样数据以简单的方式进行,然后很容易验证它们。

然后你会有一个看起来像这样的视图模型:

function SomeViewModel()
{
    this.SomeModel = new SomeModel();
}

function SomeViewModel(someModel)
{
   this.SomeModel = someModel;
}

那么你会新建它并像这样应用它:

var viewModel = new SomeViewModel();
ko.applyBindings(viewModel);

然后最后在你的HTML中你会像这样使用它:

<div data-bind="text: SomeModel.Id" />
<input data-bind="value: SomeModel.Name" />

<!-- ko with: SomeModel -->
<div data-bind="text: Id" />
<input data-bind="value: Name" />
<!-- /ko -->

那么这样你可以将你的逻辑分离到他们自己的(有争议的),然后在他们分享类似问题时轻松编写你的视图模型,然后你可以通过一个类表达你的登录问题,然后将其附加到任何viewmodel需要登录功能等。此外,这使得测试您的东西更容易,因为您可以测试POJO模型上的实际逻辑,而无需使用UI。

可能不是您问题的直接答案,但希望有助于您的设计。

答案 3 :(得分:0)

<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml">
<head>
    <title></title>
  <style type="text/css">
        .Nav
        {
            border: 1px solid #cccccc;
            border-radius: 20px;
            height: auto;
            position: absolute;
            opacity: 0.8;
            margin: auto;
            top: -20px;
            /*left: -80px;*/
            vertical-align: middle;
        }

        .aside1
        {
            border: 0px solid #cccccc;
            border-radius: 20px;
            width: 250px;
            height: auto;
            position: absolute;
            opacity: 0.8;
            margin:0 -130px -130px 0;
            top: 50%;
            right: -10px;
            bottom:-90px;
            vertical-align: middle;
        }

        ul
        {
            padding: 10px;
            margin: 0px;
        }
        li
        {
            display: block;
            padding: 20px;
            margin: 0px;
            border: 1px solid #cccccc;
            background: -webkit-linear-gradient(left top, white , brown);
            background: -moz-linear-gradient(left top, white , brown);
            background: -ie-linear-gradient(left top, white , brown);
        }
        .Content
        {
            background-color: WhiteSmoke;
            border: 1px solid #CCCCCC;
            width: 80%;
            height: 500px;

            border-radius: 0px 25px 0px 25px;

            position: relative;
            text-indent: 10px;
        }
        a
        {
            text-decoration: none;
            color: Black;
            font-family:Arial;
            font-size:12px;
            font-weight:bold;

        }

        .socialsites
        {
            content: "";
            border: 1px solid #cccccc;
            border-radius: 20px;
            height: auto;
            position: absolute;
            opacity: 0.8;
            margin: auto;
            top: 50%;
            right: 150px;
            bottom:-10px;
            vertical-align: middle;
            width: auto;
        }

        .asideul
        {
            line-height: 30px;
        }

        .asidecontent
        {
            width: 20px;
            height: 20px;
            display: block;
            opacity: 0.5;
            margin: 10px;
            padding: 10px;
            color: black;
            font-size: 20px;
            text-indent: 40px;
            -webkit-border-radius: 20px;
            -moz-border-radius: 20px;
            border-radius: 20px;
            box-shadow: 1px 0px 5px rgba(0,0,0,10);
            -webkit-transition: all 0.2s ease-in-out;
            -moz-transition: all 0.2s ease-in-out;
            -o-transition: all 0.2s ease-in-out;
            transition: all 0.2s ease-in-out;
            content: "";
            background: -webkit-linear-gradient(left top, white , brown);
            background: -moz-linear-gradient(left top, white , brown);
        }
        li.asidecontent:hover
        {
            width: 200px;
            opacity: 1;
            background: Brown;
            background-color: Brown;
        }
    </style>
</head>
<body>
<center><h1>Welcome to Patient Portal</h1></center>
<br />
<div class="Content">
        <div class="Nav">
            <ul>
                <li><a href="#">Consultation Appointment</a></li>
                <li><a href="#">Service Appointment</a></li>
                <li><a href="#">Home Care Appointment</a></li>
                <li><a href="#">Ambulance Appointment</a></li>
                <li><a href="#">Clinical History</a></li>
                <li><a href="#">Deposits</a></li>
                <li><a href="#">Vouchers</a></li>
            </ul>
        </div>
        <div class="aside1">
            <div id="socialsites">
                <ul class="asideul">
                    <li class="asidecontent">Facebook</li>
                    <li class="asidecontent">Twitter</li>
                    <li class="asidecontent">Google+</li>
                </ul>
            </div>
        </div>
    </div>
</body>
</html>