AngularJS:如何从任意类型获取属性?

时间:2014-09-20 02:32:31

标签: asp.net-mvc angularjs asp.net-web-api asp.net-mvc-5

我在这里有一些复杂的要求(一个真正令人头疼的问题)......而且我不确定最佳方式:

要求:

使用MVC5为前端构建一个用于管理AngularJS中的小部件(CMS内容块)的页面(根据管理UI的其余部分)。问题是每个小部件都有自己特定的属性集。它们都共享一些属性,如TitleIsEnabled等。但是一个HTML Widget例如将有一个BodyContent字段,一个Slider Widget将有一个图像集合等。

我的第一个想法是使用[UIHint]Html.EditorFor,以便每个窗口小部件类型都有自己的标记..我认为这非常简单,但我们怎样才能从任何部分获取属性这样的任意小部件进入AngularJS模型?

示例控制器

widgetsApp.controller('widgetController', function ($scope, $http) {
    $scope.emptyGuid = '00000000-0000-0000-0000-000000000000';

    $scope.id = $scope.emptyGuid;
    $scope.title = '';
    $scope.order = 0;
    $scope.enabled = false;
    $scope.widgetType = '';
    $scope.zoneId = $scope.emptyGuid;
    // etc
    // how to get properties of ANY widget type?

这甚至可能吗?有更好的解决方案吗?注意,如果代码可以支持我的要求,我可能会考虑更改代码以使用Knockout或其他一些此类框架。

修改

请注意,问题进一步复杂化,因为需要将这样的模型传递回服务器并在那里处理它。在常规MVC控制器中,我可以使用Request.Form来检查其他值是什么,但我使用Web API并且不确定那里是否有可能。

修改2

好的,所以我觉得我走在正确的轨道上,但仍然有问题。首先,这是我的进步:

我发现了.factory,并制作了这样的测试页:

<div ng-app="myApp">
    <div ng-controller="controller1">
        <button class="btn btn-primary" ng-click="showAllInfo()">Show Info</button>
    </div>
    <div ng-controller="controller2">
    </div>
</div>    

<script type="text/javascript">
        var myApp = angular.module('myApp', []);

        myApp.factory('widgetModel', function () {
            return {
                id: '00000000-0000-0000-0000-000000000000',
                title: '',
                order: 0,
                enabled: false,
                widgetName: '',
                widgetType: '',
                zoneId: '00000000-0000-0000-0000-000000000000',
                displayCondition: '',
                widgetValues: '',
                pageId: null,
                cultureCode: '',
                refId: null,
            };
        });

        // This is representative of the main controller
        myApp.controller('controller1', function ($scope, widgetModel) {
            $scope.emptyGuid = '00000000-0000-0000-0000-000000000000';
            $scope.model = widgetModel;
            $scope.model.id = $scope.emptyGuid;

            $scope.showAllInfo = function () {
                alert("id: " + $scope.model.id + ", New Property: " + $scope.model.myNewProperty);
            };
        });

        // This is representative of the details controller (to add properties specific to that particular widget type)
        myApp.controller('controller2', function ($scope, widgetModel) {
            $scope.model = widgetModel;
            $scope.model.myNewProperty = "My Awesome Widget";
        });
    </script>

上面的测试效果很好..但是,当我在我的实际应用程序中使用这种代码时,它无法工作,我相信的原因是因为第二个控制器稍后会被注入到DOM中。这里&#39;发生了什么:

我有一个div如下

<div ng-bind-html="widgetDetails"></div>

并在加载其他详细信息后,我为此加载了html:

$http.get("/admin/widgets/get-editor-ui/" + $scope.model.id).success(function (json) {
    $scope.widgetDetails = $sce.trustAsHtml(json.Content);
});

有效..我可以在那里看到我的新属性的html控件..以下片段是注入上面div的HTML:

<div ng-controller="widgetDetailsController">
    <div class="col-sm-12 col-md-12">
        <div class="form-group">
            @Html.Label("BodyContent", "Body Content", new { @class = "control-label" })
            @Html.TextArea("BodyContent", null, new { @class = "form-control", ng_model = "model.bodyContent", ui_tinymce = "tinyMCEOptions_BodyContent" })
        </div>
    </div>
    <button class="btn" ng-click="test()">Test</button>
</div>
<script type="text/javascript">
    widgetsApp.controller('widgetDetailsController', function ($scope, $http, widgetModel) {
        $scope.model = widgetModel;
        $scope.json = angular.fromJson($scope.model.widgetValues);
        $scope.model.bodyContent = $scope.json.bodyContent || "";

        $scope.test = function () {
            alert($scope.model.bodyContent);
        };
    });
</script>

当我点击时,&#34;测试&#34;按钮,没有任何反应......

我尝试通过此链接中列出的方法动态加载控制器:http://www.bennadel.com/blog/2553-loading-angularjs-components-after-your-application-has-been-bootstrapped.htm

它不起作用。说实话,我是AngularJS的新手,并不是真的知道它的所有内容......任何帮助都会很棒。

2 个答案:

答案 0 :(得分:0)

如果您只是想获取属性及其值,那么在AngularJS或Javascript端,您可以迭代对象属性以获取在对象上定义的所有属性。

for(var key in obj){
      $scope[key]=obj[key];
 }

在范围内,您可以使用ng-model将其绑定到视图。 这种方法可以为您提供数据,但有关数据的元数据(如用于呈现属性需求的控件)将无效。

对于高级场景,您应该尝试发送有助于在视图上呈现它的每个属性的元数据。

如果正确设置了ng-model,所有数据都将发送到服务器。

在服务器上,您可以使用dynamic关键字作为webapi方法的输入参数,并且应该有一个类似的方法来使用键值对迭代有效负载。

答案 1 :(得分:0)

我最终改为KnockoutJS,部分是因为AngularJS最终对我的需求有点过分,但也因为它无法很好地处理这种情况(或者至少没有明显和干净的方法来做它)。我的KnockoutJS解决方案如下:

在主页面中,我添加了一个html元素:

<fieldset id="widget-details"></fieldset>

要注入的任意HTML的示例:

<div id="widget-content" class="col-sm-12 col-md-12">
    <div class="form-group">
        @Html.Label("BodyContent", "Body Content", new { @class = "control-label" })
        @Html.TextArea("BodyContent", null, new { @class = "form-control", data_bind = "wysiwyg: bodyContent, wysiwygConfig: tinyMCEConfig" })
    </div>
</div>

<script type="text/javascript">
    function updateModel() {
        var data = ko.mapping.fromJSON(viewModel.widgetValues());

        viewModel.bodyContent = ko.observable("");

        if (data && data.BodyContent) {
            viewModel.bodyContent(data.BodyContent());
        }

        viewModel.tinyMCEConfig = {
            theme: "modern",
            plugins: [
                "advlist autolink lists link image charmap print preview hr anchor pagebreak",
                "searchreplace wordcount visualblocks visualchars code fullscreen",
                "insertdatetime media nonbreaking save table contextmenu directionality",
                "emoticons template paste textcolor"
            ],
            toolbar1: "insertfile undo redo | styleselect | bold italic | alignleft aligncenter alignright alignjustify | bullist numlist outdent indent | link image",
            toolbar2: "print preview media | forecolor backcolor emoticons",
            image_advtab: true,
            templates: [
                { title: 'Test template 1', content: 'Test 1' },
                { title: 'Test template 2', content: 'Test 2' }
            ],
            content_css: tinyMCEContentCss
        };
    };
    function onBeforeSave() {
        var data = {
            BodyContent: viewModel.bodyContent()
        };

        viewModel.widgetValues(ko.mapping.toJSON(data));
    };
</script>

然后在我的主页脚本中,我使用以下内容:

$.ajax({
    url: "/admin/widgets/get-editor-ui/" + self.id(),
    type: "GET",
    dataType: "json",
    async: false
})
.done(function (json) {
    var result = $(json.Content);

    var content = $(result.filter('#widget-content')[0]);
    var details = $('<div>').append(content.clone()).html();
    $("#widget-details").html(details);

    var scripts = result.filter('script');
    scripts.appendTo('body');

    // ensure the function exists before calling it...
    if (typeof updateModel == 'function') {
        updateModel();
        var elementToBind = $("#widget-details")[0];
        ko.cleanNode(elementToBind);
        ko.applyBindings(viewModel, elementToBind);
    }
})
.fail(function () {
    $.notify("There was an error when retrieving the record.", "error");
});

当我保存时,我会调用此代码:

// ensure the function exists before calling it...
if (typeof onBeforeSave == 'function') {
    onBeforeSave();
}

工作得很好。