3

I'm generating a report with collection of questions.

app.controller('reportCtrl', ['$scope','$stateParams', function ($scope, $stateParams) {

    $scope.questions: [
        { questionkey: 1, questiontext: 'Type', questiontype: 1 , questionmodel:'accsType' },
        { questionkey: 2, questiontext: 'Reported By', questiontype: 1, questionmodel: 'accsReportedBy' },
        { questionkey: 3, questiontext: 'Time and Date', questiontype: 6, questionmodel: 'accsCurrentDate' },
        { questionkey: 4, questiontext: 'Address', questiontype: 2, questionmodel: 'accsAddress' },
        { questionkey: 5, questiontext: 'Coordinates', questiontype: 6, questionmodel: 'accsCoordinates' },
        { questionkey: 6, questiontext: 'Blank', questiontype: 1, questionmodel: 'accsBlank1' },
        { questionkey: 7, questiontext: 'Blank', questiontype: 1, questionmodel: 'accsBlank2' },
        { questionkey: 8, questiontext: 'Blank', questiontype: 1, questionmodel: 'accsBlank3' },
        { questionkey: 9, questiontext: 'Blank', questiontype: 1, questionmodel: 'accsBlank4' },
        { questionkey: 10, questiontext: 'Details of Survey', questiontype: 2, questionmodel: 'accsDetailsSurvey' },
        { questionkey: 11, questiontext: 'Photos', questiontype: 5, questionmodel: 'accsPhotos' }
    ];

}]);

and I've created a custom directive to draw the questions according to the question types Eg question type 1 is text box type 2 is textarea

<question contents="questions"></question>

app.directive('question', function ($compile) {
    return {
        transclude: true,
        restrict: 'E',
        scope: {
            contents: '='
        },  
        link: function (scope, element, attrs) {   
            angular.forEach(scope.contents, function (k, v) {
                var ele;

                switch (parseInt(k.question.questiontype)) {
                    case 1:
                        ele = $compile("<accstextbox data='k.question'></accstextbox>")(scope);
                        break;
                    case 2:
                        ele = $compile("<accstextarea data='k.question'></accstextarea>")(scope);
                        break;
                }

                element.append(ele);
            });
        }         
    };
});

I've created a directive for each question types

app.directive('accstextbox', [function () {
    return {
        restrict: 'E',
        templateUrl: 'app/directives/questions/textbox.html',
        link: function (scope, element, attrs) {
           console.log(scope.data); // undefined
        },
        scope:{
            data: '='
        }
    };
}]);

app.directive('accstextarea', [function () {
    return {
        restrict: 'E',
        templateUrl: 'app/directives/questions/textarea.html',
         link: function (scope, element, attrs) {
           console.log(scope.data); // undefined
        },
        scope: {
            data: '='
        }
    };
}]);

when i dynamically add those directives, i'm passing the data object via attribute. that data object is undefined in the child directive scope. first time i'm using angularjs for my project.

huysentruitw
  • 27,376
  • 9
  • 90
  • 133
Arooran
  • 637
  • 1
  • 17
  • 31

2 Answers2

1

Your solution will not work since k is a local variable and is no accessible for the $compiler service.

A solution for this is to make your directive use ngRepeat and ngIf to generate the final layout through templates:

app.directive('question', function ($compile) {
    return {
        transclude: true,
        restrict: 'E',
        templateUrl: 'app/directives/questions.html',
        scope: {
            contents: '='
        }      
    };
});

And the app/directives/questions.html:

<div ng-repeat="question in contents">
    <accstextbox ng-if="question.questiontype == 1" data="question"></accstextbox>
    <accstextarea ng-if="question.questiontype == 2" data="question"></accstextarea>
</div>

Since this is a really small template, you may add it to the template configuration parameter of the directive, instead of loading it from the server through templateUrl.

Hope this will help you!

  • i just showed only two questions type for simplicity, but please see this question http://stackoverflow.com/questions/26093809/how-to-pass-a-data-object-to-a-dynamically-created-directive-fiddle. this is a angular js version issue? – Arooran Jan 01 '16 at 20:52
  • No, it is not. Note that the scope of `addDirective` is the same as the `MainCtrl` scope, which defines `$scope.data`. Also, this is the scope being given to the `$compiler` directive, thus making things work. This setup is a lot sensible to changes and things are not exactly encapsulated, therefore I do not recommend it. – Vinícius Gobbo A. de Oliveira Jan 01 '16 at 21:09
  • yes you are correct. i misunderstood the question answer. i'll change that with ng-if – Arooran Jan 01 '16 at 21:25
1

As Vinicius noted, you're using the k object inside the forEach loop in a text string so angular can't resolve what you mean by k.questions.

I propose a similar solution to repeat the questions in an ng-repeat inside your question directive:

<div>
  <div ng-repeat="q in contents">
    <div ng-switch="q.questiontype">
      <div ng-switch-when="1">
        <accstextbox> one: {{q.questiontext}}</accstextbox>
      </div>
      <div ng-switch-when="2">
        <accstextarea> two : {{q.questiontext}}</accstextarea>
      </div>
    </div>
  </div>
</div>

Another option is to move the logic of selecting the type of template into the child directive. I would prefer this option if your only change is the type of input, and there is not much difference in the logic, so you would avoid duplicating your code, i.e the child directive template would contain the input selection logic:

<div>
  <div ng-if="question.questiontype === 1">
    <input type="text"/>
  </div>
  <div ng-if="question.questiontype === 2">
    <textarea name="" id="" cols="30" rows="10"></textarea>
  </div>
</div>
Boris
  • 530
  • 4
  • 20
  • but i'm passing more objects to customize each control. that object contains css class, lable name, model value etc. that what i have created a seperate template html for each control and accessed via templateUrl. please see this questions http://stackoverflow.com/questions/26093809/how-to-pass-a-data-object-to-a-dynamically-created-directive-fiddle – Arooran Jan 01 '16 at 21:05
  • Those properties css class, label name, model value are all supported in both above cases. If you're suggesting that you have multiple different fields in the child directives, then it's better to use the option with an `ngSwitch` or `ngIf` in the parent directive and separate the child directives. – Boris Jan 01 '16 at 21:11