Question

I've set up a general dialog directive with a title and apply/cancel buttons. The dialog has an isolated scope.

The content of the dialog directive is transcluded and therefor it's scope is a sibling of the dialog scope:

From the Angular js docs:

However isolated scope creates a new problem: if a transcluded DOM is a child of the widget isolated scope then it will not be able to bind to anything. For this reason the transcluded scope is a child of the original scope, before the widget created an isolated scope for its local variables. This makes the transcluded and widget isolated scope siblings.

This presents a new problem for me though. The transcluded DOM should be able to respond the dialog when it is applied. Therefor I'd like to setup an 'applied' property on the dialog and let the transcluded DOM watch it. This is not possible though, because they are siblings!

Where am I going wrong?

Was it helpful?

Solution 2

Oke, I think I've found a solution. I've wrapped the actual dialog in a directive that defines the scope over the dialog. The content of the dialog is still transcluded in the dialog, but since it will take it's parent scope from the parent of the dialog (!!) and not the dialog itself (transclusion works this way), this will work quite nicely.

Further, I can have the sg-import directive respond when the dialog is applied by using a &property on the dialog. When the dialog is applied, I have it evaluate the sg-apply function in context of the parent scope (the scoping is done automatically, I just have to call the method from the controller's apply() function).

<div sg-import>
    <div 
        sg-dialog title="Import Photographs" 
        visible="show_import_dialog" 
        sg-apply="upload()"
    >
        <div class="drop-zone">
            <div sg-photo title="{{ file.name }}">
            </div>
            <input type="file" multiple />
        </div>
    </div>
</div>

OTHER TIPS

I've run into something similar and there are 2 ways (that I know of) to access the transcluded scope directly.

The first is to create the scope yourself inside a compile function and then pass it to the transclude linking function along with a clone linking function:

function compileFn(tElement, tAttrs, transclude) {
    return linkFn;
    function linkFn(scope, element, attrs) {
        scope = scope.$new();
        scope.name = attrs.works1;
        transclude(scope, function(clone) {
            element.find('div').append(clone);
        });
    };
}

The second is to create a controller and inject the $transclude service which is pre-bound to a new scope. Your clone linking function will receive the new scope as its 2nd parameter:

function Controller($element, $attrs, $transclude) {
    $transclude(function(clone, scope) {
        scope.name = $attrs.works2;
        $element.find('div').append(clone);
    });
}

In both cases you'll have to provide a clone linking function to do the transclusion yourself instead of using ngTransclude.

See http://jsfiddle.net/dbinit/wQC7G/6/ for examples of both.

If you're willing to create a model in the common ancestor to act as a switchboard with $watch targets, you can use pre-existing facilities to having each directive mutate and/or watch that switchboard model. The component's mode of access and the content content's controller have two fery idfferent calls signatures for each scope, and there is a slight "gotcha" for the transcluded case.

Isolated Scope with Bi-Directional Binding

When registering the directive's isolate scope, =attrName" will cause examination of the domainName property named "attrName". Angular will set up bi-directional binding such that a change to the value in either scope's model affect the model in the sibling scope as well.

Example

In controller-parent.js:

module.controller( 'ParentController', function() {
    $scope.switchboard = { };
}

In directive-sg-dialogue.js

return {
    scope: {
  isolatedPeer: "=dialogModel"
};

... in the directive metadata ...

<div ng-controller="ParentController">
    <sg-dialog dialog-model="switchboard">
        <div ng-controller="ChildController"></div>
    </sg-dialog>
</div>

... in some applicaton view template, and ...

$scope.switchboard = { isApplied: false } 

... in controller(s) bound to the application view template...

Then you are all set to ...

$scope.$watch( 'switchboard.isApplied', function(newValue, oldValue) { })

... in the common ancestor, and get a callback after ...

isolatedPeer.isApplied = true;

... in the isolated scope.

Prototypical Inheritance

As long as you don't explicitly set $scope.swtichboard in the transcluded child, you can access "switchboard.isApplied" from angular expressions in the transcluded child and have the interpolation engine "find" the value thaat was allocate dand stored by the parent controller in its own scope.

For example, the following callback will get invoked wheneve rthe pair dialogue box is closed:

$scope.$watch( 'switchboard.isApplied', function(newValue, oldValue) { } );

This works because the transcluded child is always given an basic scope, not an isolated scope.

Hope this is helpful!

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top