2013-03-25 33 views
8

Ho creato una direttiva per includere un plugin jQuery e ho passato un oggetto config per il plugin dal controller alla direttiva. (funziona)Condivisione dello scope tra controller e direttive in AngularJS

Nell'oggetto di configurazione è una richiamata che desidero chiamare su un evento. (funziona)

Nel callback, voglio modificare una proprietà sul $ scope del controller, che non funziona. Angular non riconosce che la proprietà è stata modificata per qualche motivo, il che mi porta a credere che l'ambito $ nella callback sia diverso da $ scope del controller. Il mio problema è che non lo faccio perché

Qualcuno può indicarmi la giusta direzione?


Click here for Fiddle


app.js

var app = angular.module('app', []) 
    .directive('datepicker', function() { 
     return { 
      restrict: 'A', 
      link: function (scope, element, attrs) { 
       // Uncommenting the line below causes 
       // the "date changed!" text to appear, 
       // as I expect it would. 
       // scope.dateChanged = true; 

       var dateInput = angular.element('.datepicker') 

       dateInput.datepicker(scope.datepickerOpts); 

       // The datepicker fires a changeDate event 
       // when a date is chosen. I want to execute the 
       // callback defined in a controller. 
       // --- 
       // PROBLEM: 
       // Angular does not recognize that $scope.dateChanged 
       // is changed in the callback. The view does not update. 
       dateInput.bind('changeDate', scope.onDateChange); 
      } 
     }; 
    }); 

var myModule = angular.module('myModule', ['app']) 
    .controller('MyCtrl', ['$scope', function ($scope) { 
     $scope.dateChanged = false; 

     $scope.datepickerOpts = { 
      autoclose: true, 
      format: 'mm-dd-yyyy' 
     }; 

     $scope.onDateChange = function() { 
      alert('onDateChange called!'); 

      // ------------------ 
      // PROBLEM AREA: 
      // This doesnt cause the "date changed!" text to show. 
      // ------------------ 
      $scope.dateChanged = true; 

      setTimeout(function() { 
       $scope.dateChanged = false; 
      }, 5000); 
     }; 
    }]); 

html

<div ng-controller="MyCtrl"> 
    <p ng-show="dateChanged">date changed!</p> 
    <input type="text" value="02-16-2012" class="datepicker" datepicker=""> 
</div> 

risposta

11

ci sono una serie di questioni di ambito al lavoro nel vostro demo. Innanzitutto, all'interno della callback dateChange, anche se la funzione stessa viene dichiarata all'interno del controller, il contesto di this all'interno del callback è l'elemento bootstrap poiché si trova all'interno di un gestore bootstrap.

Ogni volta che si modificano i valori di ambito angolare da codice di terze parti, è necessario che l'angolare lo conosca utilizzando $apply. In genere è meglio mantenere tutti gli ambiti di terze parti all'interno della direttiva.

Un approccio più angolare consiste nell'utilizzare ng-model sull'ingresso. Quindi utilizzare $.watch per le modifiche al modello. Ciò aiuta a mantenere tutto il codice all'interno del controller in un contesto angolare.È rara in qualsiasi applicazione angolare di non utilizzare ng-model qualsiasi forma controlla

<input type="text" class="datepicker" datepicker="" ng-model="myDate"> 

Entro direttiva:

dateInput.bind('changeDate',function(){ 
     scope.$apply(function(){ 
     scope[attrs.ngModel] = element.val() 
     }); 
}); 

Poi nel controller:

$scope.$watch('myDate',function(oldVal,newVal){ 
     if(oldVal !=newVal){ 
      /* since this code is in angular context will work for the hide/show now*/ 
      $scope.dateChanged=true; 
      $timeout(function(){ 
        $scope.dateChanged=false; 
       },5000); 
     }   
}); 

Demo: http://jsfiddle.net/qxjck/10/

EDIT Un altro elemento che dovrebbe cambiare è rimuovere var dateInput = angular.element('.datepicker') se si desidera utilizzare questa direttiva su più di un elemento nella pagina. È ridondante utilizzato nella direttiva in cui element è già uno degli argomenti nel callback link ed è specifico dell'istanza. Sostituisci dateInput con element

+0

un altro pensiero ... ci sono diversi progetti di direttive ui angular/bootstrap che sono super facili da usare su github – charlietfl

+0

Grazie per avermi sistemato. L'ho fatto funzionare immediatamente con $ apply (potrei abbracciarti per questo), ma voglio farlo nel modo giusto, quindi sto cercando di implementare l'approccio che hai mostrato. Qualcosa che non ho mostrato, ma che dovrebbe avere nella mia demo, in realtà sto usando un tag * * per attivare il selettore di date. La data scelta è conservata in 'element.data ('datepicker'). GetDate()'. Sono nuovo di AngularJS, quindi scusami se ho torto, ma ng-model è per gli elementi di forma. Quale sarebbe un sostituto adatto? – simshaun

+0

non è familiare con bootstrap datepicker come lo sono con jQuery Ui one. Tuttavia, invece di usare 'ng-model' su un input, è possibile codificare la variabile per aggiornare l'ambito del controller, o utilizzare qualsiasi attributo personalizzato che si desidera memorizzare il nome della variabile (se si utilizza questo in più punti della pagina, ad esempio). Hai più problemi, crea una versione aggiornata con l'implementazione del tag ''. Fonte di valore come dati su elemnt facili da modificare nella chiamata 'scope. $ Apply' in direttiva. Mantieni lo stesso metodo '$ watch'. – charlietfl

2

L'evento changeDate associato all'ingresso sembra essere impostato per sparare all'esterno del framework Angular. Per mostrare il paragrafo, chiamare $scope.$apply() dopo aver impostato dateChanged su true. Per nascondere il paragrafo dopo il ritardo, è possibile utilizzare nuovamente $apply() all'interno della funzione passata a setTimeout, ma è probabile che non si verifichino ulteriori problemi utilizzando invece lo $timeout() di Angular.

Fiddle

Problemi correlati