简体   繁体   中英

Mixing Angular with MVC partial views

What I need

A chain of screens that each open the next screen on a button click. Each previous screen must be collapsed and the new screen must be added by loading a partial view from the MVC backend.

What I have

An AngularJS controller with the following function:

self.AddChild = function (uri, targetContainerId, collapseTitle, breadCrumbContainerId) {
    var target = $("#" + targetContainerId);

    if (target != 'undefined' && target != undefined && target.length > 0) {
        apiService.Get(uri).then(function (viewData) {
            self.CollapsePreviousChild(self.ChildCount);

            // Increase childcount by 1
            self.ChildCount += 1;

            // Set HTML data
            var html = '<div id="collapsibleScreen-"' + self.ChildCount + ' class="open">' + viewData + '</div>';

            target.html(html);

            // Update screens collapse status
            self.UpdateScreenBreadCrumb(collapseTitle, breadCrumbContainerId);
        });
    };
}

The UpdateScreenBreadCrumb function works and is otherwise unrelated.

It is called (for instance) like this:

self.AddChild("/Partials/View1", "targetContainer", "View", "breadCrumbContainer");

What it does

The content of the View, a form, is loaded, the breadcrumb is updated correctly.

What I need fixed

On the partial view that was loaded, there is a button defined like this:

<button class="btn btn-primary" ng-click="AddPartialView()">Add partial view</button>

Clicking that button has no effect whatsoever. If I add a console.log('Code was here.') to the AddPartialView(), it is not logged. Setting the ng-click value directly to alert('hello') has no effect either.

There are no errors of any kind visible.

Any suggestions on how to make this button work?

In regards to your question, you are adding HTML that isn't compiled by Angular. You need to use $compile on your newly added HTML element and then bind it to a scope. The $compile() function returns a link() function which you use to bind a scope to. Example:

$compile(new-element)(scope-to-bind-to)

NOTE : You should not be manipulating the DOM via a controller. This is considered bad practice. You should be using a custom directive or some combo of Angular directives (ngIf, ngSwitch, ngInclude). I recommend watching AngularJS best practices .

I've looked into $compile, as suggested by Itamar L. and got it to work. The samples I found were using Directives as well, so I implemented them anyway:

angular.module('directives.api').directive("PartialViewLoader", [
    '$compile',
    'chainedScreensService',
    function (
        $compile,
        chainedScreensService) {
        return {
            restrict: 'A',
            scope: {
                view: '=',
                parent: '='
            },
            controller: function() {
            },
            link: function (scope, element, attrs) {

                chainedScreensService.GetPartialView(attrs.view).then(function (viewData) {
                    var linkFunc = $compile(viewData);
                    var content = linkFunc(scope);
                    element.append(content);

                    if (attrs.parent != 'undefined' && attrs.parent != undefined && attrs.parent.length > 0) {
                        chainedScreensService.CollapsePartialByIdentifier(attrs.parent);
                    }
                });
            }
        }
    }
]);

I use it like this:

<div ng-controller="collapseController">
    <div id="breadCrumbContainer" style="display: inline"></div>
    <div id="mainContainer">
        <div id="personContainer" partial-view-loader view="persoon" parent="" class="open"></div>
    </div>
</div>

That in itself displays the first page, which has a button to the next, as mentioned. The associated function, found in the collapseController, is this:

self.AddNextScreen = function (parentViewIdentifier, targetContainerId, breadCrumbContainerId) {
    self.AddChildByDirective("NextScreen", parentViewIdentifier, targetContainerId, breadCrumbContainerId);
}

The code for AddChildByDirective:

self.AddChildByDirective = function (viewIdentifier, parentViewIdentifier, targetContainerId, breadCrumbContainerId) {
    var html = '<div id="' + viewIdentifier + 'Container" fvl-partial-view-loader view="' + viewIdentifier + '" parent="' + parentViewIdentifier + '" class="open"></div>';

    var target = $('#' + targetContainerId);

    var linkFunc = $compile(html);
    var content = linkFunc($scope);
    target.append(content);

    self.UpdateScreenBreadCrumb(viewIdentifier, breadCrumbContainerId);
}

At this point I still need to test actual chaining, but this works to load a new screen and collapse the previous.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM