Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

AngularJS Directive - Call a specific function when an input file changes

I am creating a scavenger hunt editor that will contain many different questions. Also, each question can be of different type. Therefore, I have one array of questions and I do an ng-repeat to show all the questions.

To do so, I have a javascript object that represent a Question. This question can be inherited by different question types.

I have one specific question type, that is slidingPuzzle, which will require an image to be uploaded. My problem arrives here, I want to be able to call a function, inside this object, when the input file changes.

I want here to put the emphasis on the fact that it cannot be a general function declared in the scope !

Here's how my structure looks like (http://plnkr.co/edit/lnJ8rAlpP0xJ3aM2HZ7o?p=preview):

HTML:

      <div class="question" ng-repeat="question in scavengerHunt.questions">

        <div class="chooseQuestionType">
          Question type:
          <select ng-change="question.setChild()" ng-model="question.subClass">
            <option value="slidingPuzzleQuestion">slidingPuzzleQuestion</option>
          </select>
        </div>
        <div class="questionContainer">
            <div class="puzzleContainer">

                  <input type="file" name="image" id="puzzleContainerImage" ng-file-select="question.child.imageFileSelected()">

            </div>
        </div>

AngularJS Model:

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

scavengerHuntApp.controller('QuestionsCtrl', function ($scope) {
  function ScavengerHunt() {
                var self = this;

                self.questions = [];


                self.addQuestion = function(question) {
                    self.questions.push(question);
                }

            }
  function Question() {
                var self = this;

                self.id = 0;
                self.subClass = "slidingPuzzleQuestion";


                self.child = "";

                self.setChild = function() {
                    var type = self.subClass;

                    if(type == 'slidingPuzzleQuestion'){
                        self.child = new SlidingPuzzleQuestion();
                    }

                }
            }

            function SlidingPuzzleQuestion() {
                var self = this;


                self.imageFileSelected = function () {
                    console.log()
                };



            }


            //Utilities function
            $scope.addEmptyQuestion = function() {
                $scope.scavengerHunt.addQuestion(new Question());
            }

            $scope.scavengerHunt = new ScavengerHunt();

            $scope.addEmptyQuestion();

});

AngularJS Directive

Up to now, I figured that I would probably have to use an AngularJS directive, since the ng-change does not work currently with input file (https://github.com/angular/angular.js/issues/1375). Hence, the following code does not work since in the html tag ng-file-select="question.child.imageFileSelected()" , I have a context that is lost when trying to bind the element to a function.

  scavengerHuntApp.directive("ngFileSelect",function(){

            return {
                link: function (scope, element, attrs) {

                    console.log(scope);
                    console.log(element);
                    var onChangeFunc = element.scope()[attrs.customOnChange];
                    element.bind('change', onChangeFunc); 

                }

            }


        });

So, is there a way to pass the current context in the html code to bind it to a particular object ?

I hope everything is clear enough, if not let me know and I'll clarify as much as possible !

like image 441
Etienne Noël Avatar asked Oct 02 '22 11:10

Etienne Noël


1 Answers

Ok, have a look at this plunker: http://plnkr.co/edit/qPK6EmAkt39QoZFZ5Sa3?p=preview

I have removed some of your code just to let you understand what's going on low level.

You can use isolated scope with callback attribute binding using & symbol. What it does is it registers the expression in the attribute as a callable function inside the isolated scope of the directive.

The rules are:

  1. You register isolated scope

    scope : {
        myAttr : "&"
    }
    
  2. You specify the attribute in the form

    my-attr="object.objectMethod(param1, param2)"
    
  3. Angular wraps the objectMethod with the callable wrapper

  4. When needed, you call the wrapper like this:

    scope.myAttr({param1: "value", param2:"value"})
    
  5. Angular transforms this call into actual object.objectMethod call with parameters specified
like image 93
Mr_Mig Avatar answered Oct 19 '22 01:10

Mr_Mig