57

Я попытался написать небольшую директиву, чтобы обернуть ее содержимое другим файлом шаблона.Директива AngularJS не обновляет изменения переменных области

Этот код:

<layout name="Default">My cool content</layout> 

Должен иметь этот выход:

<div class="layoutDefault">My cool content</div> 

Поскольку раскладка "по умолчанию" имеет следующий код:

<div class="layoutDefault">{{content}}</div> 

Здесь код директивы:

app.directive('layout', function($http, $compile){ 
return { 
    restrict: 'E', 
    link: function(scope, element, attributes) { 
     var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default'; 
     $http.get(scope.constants.pathLayouts + layoutName + '.html') 
      .success(function(layout){ 
       var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g; 
       var result = regexp.exec(layout); 

       var templateWithLayout = result[1] + element.html() + result[2]; 
       element.html($compile(templateWithLayout)(scope)); 
      }); 
    } 
} 

});

Моя проблема:

Когда я использую области видимости переменных в шаблоне (в шаблоне макета или внутри макета тега), например. {{whatever}} он просто работает изначально. Если я обновляю переменную whatever, директива больше не обновляется. Вся функция соединения будет только активирована один раз.

Я думаю, что AngularJS не знает, что эта директива использует переменные области видимости и поэтому не обновляется. Но я не знаю, как исправить это поведение.

+2

Ни один из нынешних ответов не кажется, для решения вопроса о том, почему (при использовании '$ compile') часы не автоматически устанавливается. Как вы говорите, он связан сначала ... –

+0

Я нашел другое решение, чтобы использовать шаблон и ng-transclude. Это хорошо работает - всегда. Единственная проблема заключается в том, что я не знаю, как настроить сам шаблон макета. Если я использую ng-include с функцией scope для получения пути к шаблону, я получаю ошибку ngTransclude: orphan. – Armin

+1

Хорошо, я нашел решение для изменения templateUrl динамически. См. Мой собственный ответ ниже. – Armin

ответ

73

Вы должны создать связанную переменную области действия и следить за его изменениями:

return { 
    restrict: 'E', 
    scope: { 
    name: '=' 
    }, 
    link: function(scope) { 
    scope.$watch('name', function() { 
     // all the code here... 
    }); 
    } 
}; 
+2

да, функция $ watch() сделала волшебство для меня !! – JayKandari

+2

Совет стиля: правильное имя переменной должно быть «scope», а не '$ scope'. Внутри функции 'link' область видимости является простой переменной. – adelriosantiago

+0

спасибо @adelriosantiago – QuarK

16

Вы должны сказать Угловое, что ваша директива использует переменную области видимости:

Вам нужно связать некоторое свойство рамки для вашей директивы:

return { 
    restrict: 'E', 
    scope: { 
     whatever: '=' 
    }, 
    ... 
} 

и затем $watch это:

$scope.$watch('whatever', function(value) { 
    // do something with the new value 
    }); 

Для получения дополнительной информации см. Angular documentation on directives.

2

Вы должны держать часы на области.

Вот как вы можете это сделать:

<layout layoutId="myScope"></layout> 

Ваша директива должна выглядеть

app.directive('layout', function($http, $compile){ 
    return { 
     restrict: 'E', 
     scope: { 
      layoutId: "=layoutId" 
     }, 
     link: function(scope, element, attributes) { 
      var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default'; 
      $http.get(scope.constants.pathLayouts + layoutName + '.html') 
       .success(function(layout){ 
        var regexp = /^([\s\S]*?){{content}}([\s\S]*)$/g; 
        var result = regexp.exec(layout); 

        var templateWithLayout = result[1] + element.html() + result[2]; 
        element.html($compile(templateWithLayout)(scope)); 
     }); 
    } 
} 

$scope.$watch('myScope',function(){ 
     //Do Whatever you want 
    },true) 

Точно так же вы можете модели в вашей директиве, так что, если обновление модели автоматически ваш метод часы обновит директивы.

+0

ключ должен использовать = на переменной области и установить параметр thrid $ watch как истинный. (примечание @ о переменной области не будет работать) –

8

Я нашел гораздо лучшее решение:

app.directive('layout', function(){ 
    var settings = { 
     restrict: 'E', 
     transclude: true, 
     templateUrl: function(element, attributes){ 
      var layoutName = (angular.isDefined(attributes.name)) ? attributes.name : 'Default'; 
      return constants.pathLayouts + layoutName + '.html'; 
     } 
    } 
    return settings; 
}); 

Единственный недостаток я вижу в настоящее время, является тот факт, что шаблоны получили включены через свои собственные рамки. Они получают значения от своих родителей, но вместо того, чтобы изменять значение в родительском, значение будет храниться в собственной, новой области.Чтобы этого избежать, теперь я использую $parent.whatever вместо whatever.

Пример:

<layout name="Default"> 
    <layout name="AnotherNestedLayout"> 
     <label>Whatever:</label> 
     <input type="text" ng-model="$parent.whatever"> 
    </layout> 
</layout> 
39

Я нуждался в решении этого вопроса, а также и я использовал ответы в этой теме, чтобы придумать следующее:

.directive('tpReport', ['$parse', '$http', '$compile', '$templateCache', function($parse, $http, $compile, $templateCache) 
    { 
     var getTemplateUrl = function(type) 
     { 
      var templateUrl = ''; 

      switch (type) 
      { 
       case 1: // Table 
        templateUrl = 'modules/tpReport/directives/table-report.tpl.html'; 
        break; 
       case 0: 
        templateUrl = 'modules/tpReport/directives/default.tpl.html'; 
        break; 
       default: 
        templateUrl = ''; 
        console.log("Type not defined for tpReport"); 
        break; 
      } 

      return templateUrl; 
     }; 

     var linker = function (scope, element, attrs) 
     { 

      scope.$watch('data', function(){ 
       var templateUrl = getTemplateUrl(scope.data[0].typeID); 
       var data = $templateCache.get(templateUrl); 
       element.html(data); 
       $compile(element.contents())(scope); 

      }); 



     }; 

     return { 
      controller: 'tpReportCtrl', 
      template: '<div>{{data}}</div>', 
      // Remove all existing content of the directive. 
      transclude: true, 
      restrict: "E", 
      scope: { 
       data: '=' 
      }, 
      link: linker 
     }; 
    }]) 
    ; 

Включите в свой HTML :

<tp-report data='data'></tp-report> 

Эта директива используется для динамических чтобы загрузить шаблоны отчетов на основе набора данных, полученного с сервера.

Он устанавливает часы в свойстве scope.data и всякий раз, когда это обновляется (когда пользователи запрашивают новый набор данных с сервера), он загружает соответствующую директиву для отображения данных.

+0

Функция $ watch()! всегда искать. !! – JayKandari

+0

Я ответил в этой теме –

0

Я знаю, что это старый предмет, но в случае какой-либо считает, что это, как и я:

Я использовал следующий код, когда я нуждался в моих директивах для обновления значений, когда «родительская область» обновлено. Пожалуйста, во что бы то ни стало поправьте меня, если я что-то делаю неправильно, поскольку я все еще участвую в угловатости, но это делало то, что мне нужно;

директива:

directive('dateRangePrint', function(){ 
    return { 
     restrict: 'E', 
     scope:{ 
     //still using the single dir binding 
      From: '@rangeFrom', 
      To: '@rangeTo', 
      format: '@format' 
     }, 
     controller: function($scope, $element){ 

      $scope.viewFrom = function(){ 
        return formatDate($scope.From, $scope.format); 
       } 

      $scope.viewTo = function(){ 
        return formatDate($scope.To, $scope.format); 
       } 

      function formatDate(date, format){ 
       format = format || 'DD-MM-YYYY'; 

       //do stuff to date... 

       return date.format(format); 
      } 

     }, 
     replace: true, 
     // note the parenthesis after scope var 
     template: '<span>{{ viewFrom() }} - {{ viewTo() }}</span>' 
    } 
}) 
0

Я не уверен, почему никто до сих пор не предложил bindToController, который удаляет все эти уродливые scopes and $watches. Если Вы используете Angular 1.4

Ниже приведен пример DOM:

<div ng-app="app"> 
    <div ng-controller="MainCtrl as vm"> 
     {{ vm.name }} 
     <foo-directive name="vm.name"></foo-directive> 
     <button ng-click="vm.changeScopeValue()"> 
     changeScopeValue 
     </button> 
    </div> 
</div> 

controller Код:

angular.module('app', []); 

// main.js 
function MainCtrl() { 
    this.name = 'Vinoth Initial'; 
    this.changeScopeValue = function(){ 
     this.name = "Vinoth has Changed" 
    } 
} 

angular 
    .module('app') 
    .controller('MainCtrl', MainCtrl); 

// foo.js 
function FooDirCtrl() { 
} 

function fooDirective() { 
    return { 
     restrict: 'E', 
     scope: { 
      name: '=' 
     }, 
     controller: 'FooDirCtrl', 
     controllerAs: 'vm', 
     template:'<div><input ng-model="name"></div>'   
    }; 
} 

angular 
    .module('app') 
    .directive('fooDirective', fooDirective) 
    .controller('FooDirCtrl', FooDirCtrl); 

Скриншот, чтобы поиграть, здесь мы меняем значение области в controller и автоматически directive updates on scope change. http://jsfiddle.net/spechackers/1ywL3fnq/

+0

Вы даже не используете bindToController в своем примере. – Suamere

0

Мы можем попробовать это

$scope.$apply(function() { 
    $scope.step1 = true; 
    //scope.list2.length = 0; 
}); 

http://jsfiddle.net/Etb9d/

+1

Хотя этот фрагмент кода может решить вопрос, [включая объяснение] (http://meta.stackexchange.com/questions/114762/explaining-entirely-code-based-answers) действительно помогает улучшить качество вашего после. Помните, что вы отвечаете на вопрос читателей в будущем, и эти люди могут не знать причин вашего предложения кода. –

Смежные вопросы