kbd
kbd

Reputation: 4449

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?

Upvotes: 0

Views: 1802

Answers (2)

kbd
kbd

Reputation: 4449

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.

Upvotes: 1

Itamar L.
Itamar L.

Reputation: 519

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.

Upvotes: 1

Related Questions