0

So, my goal is to ng-repeat over an array of activities and display a specific directive based off of the activity type. Right now, I'm just testing the idea out to see if it is viable. I can display a directive dynamically, but the key is I want to have two way binding between the activities in the main.js and the directive that is displayed.

main.html

<div ng-controller="mainCntrl">
  <div>Activity: { <div>isIncluded: {{activities[0].isIncluded}}</div> }</div>
  <dynamic-directive type="{{activities[0].type}}" attributes="instance='activities[0]'"></dynamic-directive>
</div>

main.js

define(['angularAMD', 'dynamicDirective', 'activity1'],
    function (angularAMD) {
        'use strict';

        var app = angular.module('mainModule', []);

        app.controller('mainCntrl', ['$rootScope', '$scope',
            function ($rootScope, $scope) {
                $scope.activities = [{
                    type: "activity1",
                    isIncluded: true
                }];
            }]);
    });

dynamicDirective.js

define('dynamicDirectiveModule', ['angularAMD'], function (angularAMD) {
    'use strict';
    var app = angular.module('dynamicDirectiveModule', []);

    app.directive('dynamicDirective', ['$compile',
        function ($compile) {
            return {
                restrict: 'E',
                scope: {
                    type: '@',
                    attributes: '@'
                },
                link: function (scope, element) {
                    var generatedDirective = '<' + scope.type + ' ' + scope.attributes + '></' + scope.type + '>';
                    element.append($compile(generatedDirective)(scope));
                }
            };
        }
    ]);
});

activity1.js

define('activity1Module', ['angularAMD'], function (angularAMD) {
    'use strict';
    var app = angular.module('activity1Module', []);

    app.controller('activity1Cntrl', ['$scope',
        function ($scope) {
            console.log("$scope.instance: " + $scope.instance);
            $scope.thisInstance = $scope.instance || {};
        }
    ]);

    app.directive('activity1', [
        function () {
            return {
                restrict: 'E',
                templateUrl: 'processes/templates/Activity1',
                controller: 'activity1Cntrl',
                scope: {
                    instance: '='
                }
            };
        }
    ]);
});

activity1.html

<div>
  <div>ISINCLUDED: {{thisInstance.isIncluded}}</div>
  <button ng-model="thisInstance.isIncluded" value="true">Yes</button>
  <button ng-model="thisInstance.isIncluded" value="false">No</button>
</div>

With the way it is set up now, the console.log outputs that $scope.instance is undefined. So, $scope.thisInstance.isIncluded defaults to false. If, in the main.html, I set attributes="instance='{{activities[0]}}'", $scope.thisInstance.isIncluded correctly is set to true, but I have no two-way binding as instead of activities[0] being passed in as essentially a pointer, it passes the value, { type: "activity1", isIncluded: true }. How can I get the two-way binding to work? Is it possible? Is there a better way to do this?

ScubaSteve
  • 7,724
  • 8
  • 52
  • 65
  • Have you tried ng-include? May not that fancy but much simplier – Petr Averyanov May 31 '17 at 16:47
  • I did look into it, but from what I could tell, ng-include only compiles the html. Instead, I need a true directive as, although my example above only has two buttons, I intend on having more functionality like saving the activities that I want to be reusable. – ScubaSteve May 31 '17 at 17:04
  • scope: { type: '=', attributes: '=' } Write your scope like that – Yash Kochar Jun 01 '17 at 07:03
  • @YashKochar, I tried that, but $scope.instance still comes in as undefined in the activity1 directive. – ScubaSteve Jun 01 '17 at 11:51
  • Try to change the data format of attributes from just string to a JSON Object `attributes="instance='activities[0]'"` instead do this `attributes="{ key: 'instance', value: activities[0] }"` And take care of the object in another directive respectively. – Yash Kochar Jun 01 '17 at 12:37
  • @YashKochar I tried this, but it ends up putting `instance="[object Object]"` which throws a parsing error. The code in my question correctly puts `instance="activities[0]"` into the tag. That's how I would want it to look if I skipped the dynamic directive. ``. It would correctly create a two-way binding between activities[0] in the main.js with the instance in the activity1.js. For some reason, the dynamicDirective.js does not create this connection with the same tag. – ScubaSteve Jun 01 '17 at 13:28

1 Answers1

0

My current workaround for this is to utilize ng-switch and bypass dynamicDirective.js.

main.html

<div ng-switch="activity.type" ng-repeat="activity in activities">
    <activity1 ng-switch-when="activity1" instance="activity"></activity1>
    <activity2 ng-switch-when="activity2" instance="activity"></activity2>
</div>

This is not optimal as it could get ugly fast. I'm not going to mark this as the answer as it is just a workaround.

ScubaSteve
  • 7,724
  • 8
  • 52
  • 65