2013-05-10 3 views
38

Noob angolare qui. Sto creando una direttiva per visualizzare ricorsivamente un albero di domande e domande secondarie. Sto usando un collegamento nel modello che chiama una funzione all'interno dell'ambito. Per qualche ragione, non chiama il metodo editQuestion().ng-clic non funziona all'interno del modello di una direttiva

Ecco il codice e il violino http://jsfiddle.net/madhums/n9KNv/

HTML:

<div ng-controller="FormCtrl"> 
    <questions value="survey.questions"></questions> 
</div> 

Javascript:

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

function FormCtrl ($scope) { 
    $scope.editQuestion = function (question) { 
    alert('abc'); 
    }; 
    $scope.survey = { 
    // ... 
    } 
} 


app.directive('questions', function($compile) { 
    var tpl = '<ol ui-sortable' + 
    ' ng-model="value"' + 
    ' class="list">' + 
    ' <li ng-repeat="question in value | filter:search"' + 
    '  <a href="" class="question">' + 
    '  {{ question.name }}' + 
    '  </a>' + 
    '  <span class="muted">({{ question.type }})</span>' + 
    '  <a href="" class="danger" ng-click="removeQuestion(question)">remove</a>' + 
    '  <a href="" class="blue" ng-click="editQuestion(question)">edit</a>' + 
    '  <choices value="question.choices"></choices>' + 
    ' </li>' + 
    '</ol>'; 

    return { 
    restrict: 'E', 
    terminal: true, 
    scope: { value: '=' }, 
    template: tpl, 
    link: function(scope, element, attrs) { 
     $compile(element.contents())(scope.$new()); 
    } 
    }; 
}); 

app.directive('choices', function($compile) { 
    var tpl = '<ul class="abc" ng-repeat="choice in value">'+ 
    ' <li>' + 
    ' {{ choice.name }}' + 
    ' <span class="muted">' + 
    '  ({{ choice.questions.length }} questions)' + 
    ' </span>' + 
    '' + 
    ' <a href=""' + 
    '  ng-click="addQuestions(choice.questions)"' + 
    '  tooltip="add sub questions">' + 
    '  +' + 
    ' </a>' + 
    '' + 
    ' <questions value="choice.questions"></questions>' 
    ' </li>' + 
    '</ul>'; 

    return { 
    restrict: 'E', 
    terminal: true, 
    scope: { value: '=' }, 
    template: tpl, 
    link: function(scope, element, attrs) { 
     $compile(element.contents())(scope.$new()); 
    } 
    }; 
}); 

Qualsiasi aiuto nella comprensione di questo sarebbe apprezzato.

risposta

38

Hai un problema di ambito. Poiché hai utilizzato l'ambito isolato nella tua direttiva con scope: { value: '=' }, non ha più accesso all'ambito del controller che dispone di editQuestion.

È necessario passare editQuestion insieme all'ambito della propria direttiva in modo che sappia come chiamarlo. Questo è in genere piuttosto semplice, ma a causa della struttura direttiva infinitamente ricorsiva in cui le scelte possono includere domande, diventa un po 'più complicato. Ecco un violino di lavoro:

http://jsfiddle.net/n9KNv/14/

L'HTML include ora un riferimento alla editQuestion:

<div ng-controller="FormCtrl"> 
    <questions value="survey.questions" on-edit="editQuestion(question)"></questions> 
</div> 

E la tua direttiva domande si aspetta ora un attributo onEdit nel suo campo di applicazione:

app.directive('questions', function($compile) { 
    var tpl = '<ol ui-sortable' + 
    ' ng-model="value"' + 
    ' class="list">' + 
    ' <li ng-repeat="question in value | filter:search"' + 
    '  <a href="" class="question">' + 
    '  {{ question.name }}' + 
    '  </a>' + 
    '  <span class="muted">({{ question.type }})</span>' + 
     '  <a href="" class="blue" ng-click="onEdit({question: question})">edit</a>' + 
     '  <choices value="question.choices" on-edit="onEdit({question: subQuestion})"></choices>' + 
    ' </li>' + 
    '</ol>'; 

    return { 
    restrict: 'E', 
    terminal: true, 
     scope: { value: '=', onEdit: '&' }, 
    template: tpl, 
    link: function(scope, element, attrs) { 
     $compile(element.contents())(scope.$new()); 
    } 
    }; 
}); 

app.directive('choices', function($compile) { 
    var tpl = '<ul class="abc" ng-repeat="choice in value">'+ 
    ' <li>' + 
    ' {{ choice.name }}' + 
    ' <span class="muted">' + 
    '  ({{ choice.questions.length }} questions)' + 
    ' </span>' + 
    '' + 
     ' <questions value="choice.questions" on-edit="onEdit({subQuestion: question})"></questions>' 
    ' </li>' + 
    '</ul>'; 

    return { 
    restrict: 'E', 
    terminal: true, 
     scope: { value: '=', onEdit: '&' }, 
    template: tpl, 
    link: function(scope, element, attrs) { 
     $compile(element.contents())(scope.$new()); 
    } 
    }; 
}); 

Nota come stiamo mirando a question nello ng-click. Questo è il modo in cui si indirizzano gli argomenti nelle funzioni di callback. Inoltre, notiamo come nel on-edit stiamo passando alla direttiva choices, il nostro targeting è subQuestion. Questo perché question è già prenotato all'interno dello ngRepeat, quindi è necessario distinguere tra i due.

Questo è stato probabilmente il concetto più difficile per me per imparare in Angular finora. Una volta compreso come funziona lo scope tra controller, direttive e altre direttive, il mondo di Angular è tuo.:)

+0

Grazie. Mi sono sbagliato aggiungendo il comando ng-click = "onEdit()" alla direttiva. – Aravind

+0

@Langdon, grazie per aver condiviso. Questo mi ha aiutato a risolvere il problema che stavo avendo. Nel mio modello, avevo e continuava a dare errori del parser fino a quando non ho eliminato le parentesi attorno a twowaybound.value dopo aver visto il tuo esempio. –

8

Questo è un problema di ambito. Il comando ng-click della direttiva chiama i metodi removeQuestion dello scope corrente & removeQuestion, che non esistono nell'ambito della direttiva, così come sono definiti nel modulo che include la direttiva (cioè l'ambito genitore).

Si desidera stabilire un bind tra la direttiva e il genitore, quindi quando la direttiva chiama la funzione ngClick, si attiva sul modulo che ospita la direttiva.

entrambi i casi è possibile definire i metodi nella direttiva stessa, o vincolante attraverso la sezione campo di applicazione della directive definition object

Qui l'installazione di un plunker che illustra cottura ng clic eventi in diversi ambiti (uscite per console)

http://plnkr.co/edit/9XfXCpU6lhUOqD6nbVuQ?p=preview

2

Langdon's May10 '13 risposta è corretta. A scopo dimostrativo, ho semplificato il codice del violino di Langdon e l'ho portato da 148 linee angolari a 23 linee angolari.
Inoltre, ho aggiunto funzionalità che consentono il passaggio di un valore di parametro come oggetto MouseEvent tramite il metodo di chiamata della funzione e il recupero di tale valore nella funzione.

Ecco lo JSFIDDLE seguito dal codice e dai crediti, dovrebbe essere molto facile da seguire.

http://jsfiddle.net/BeyondLogical/evjzoo30/

--Html--

<div ng-controller="FormCtrl"> 
    <questions on-edit="editQuestion(ev,question)" ></questions> 
</div> 

--AngularJS--

var app = angular.module('myApp', []); 
function FormCtrl ($scope) { 
    $scope.editQuestion = function (ev,question) { 
     //ev returns a MouseEvent object 
     alert("ev: " + ev); 
     //this is how you get the 'data' attribute set in the span tag below 
     alert("ev-data: " + ev.target.attributes.data.value); 
    }; 
} 
app.directive('questions', function($compile) { 
    var tpl = 
    '<span ng-click="onEdit({ev: $event, myName: question})" data="This sentence would probably be replaced with a mustache brace parameter, example: {{someValue}}, returning a value from the scope." style="cursor:pointer;">Click Me</span>'; 
    return { 
     restrict: 'E', 
     terminal: true, 
     scope: { onEdit: '&' }, 
     template: tpl, 
     link: function(scope, element, attrs) { 
      $compile(element.contents())(scope.$new()); 
     } 
    }; 
}); 

crediti verso,
Langdon - ng-click doesn't work within the template of a directive

Mark Rajcok - AngularJS getting $event from a directive (Langdo n ottiene anche un assist per porre la domanda marchio risposte)

PavanAsTechie - Access attribute value inside non-directive controller function e JSFIDDLE di Pavan - http://jsfiddle.net/brettdewoody/FAeJq/ (in particolare seguente riga di codice di Pavan): alert(obj.target.attributes.data.value);

1

a nessuno di venire a questo con e cercando di farlo con il codice non è in esecuzione nella direttiva, controllare che non si stia utilizzando l'opzione sostituire.

Esempio:

angular.module('app').directive('myDirective', function() { 
return { 
    template: '<div ng-click="clicked()"></div>', 
    scope: { 
     options: "=" 
    }, 
    replace: true, //<---- Change this to false 
    restrict: 'E', 
    controller: function ($scope) { 

     $scope.clicked = function(){ 
     console.log("Clicked"); 
     } 
    } 
    }; 
}