如何在加载视图后获取指令?

时间:2013-05-13 18:13:09

标签: angularjs angularjs-directive photoswipe

我知道这已被问了一千次,但我想我已经尝试了所有我已经阅读的解决方案,而且我似乎无法让它发挥作用。

我有一个API,我从中获取图像,将图像拉入视图后,我希望他们使用photoswipe在手机上显示。

我有一个指令:

'use strict';

App.directive('photoswipe', function () {
return {
    replace: false,
    restrict: 'A',
    link: function photoSwipeLink(scope, element, attr) {
      scope.$watch(attr.photoswipe, function(value){
        angular.element('#gallery a').photoSwipe({
          enableMouseWheel: false,
          enableKeyboard: false
        });
      });
    }
  };
});

我在控制台中收到此错误:

Code.PhotoSwipe.createInstance: No images to passed.

我想这是因为该指令在视图渲染之前正在运行。

如果我添加$ timeout而不是watch,那么代码确实有效。不幸的是,这个解决方案并不好,因为从API获取数据可能会有延迟。此外,代码在超时时间内不起作用。

我试过,上面的代码,我尝试过使用$ viewcontentloaded,在ng-repeat中的最后一个元素之后尝试了触发函数,但都没有工作。

非常感谢任何帮助。

编辑:

这是HTML。

<h1>{{ gallery.headline }}</h1>
<ul id="gallery" photoswipe>
  <li class="gallery" ng-repeat="image in gallery.images">
    <a href="{{ image.url }}" alt="image.caption">
      <img ng-src="{{ image.thumb_url }}" class="thumb" alt="{{ image.caption }}" style="display: block;">
      <h3 class="headline">{{ image.caption }}</h3>
    </a>
  </li>
</ul>

3 个答案:

答案 0 :(得分:3)

我认为这种情况正在发生,因为Angular在结果绑定到列表之前正在处理您的指令。这可能是竞争条件。解决这个问题的一种方法是做

$scope.$broadcast("picsDownloaded" ... 
获得结果后,在控制器中发生

事件。 在指令上,而不是

scope.$watch(attr.photoswipe ....

这样做

scope.$on("picsDownloaded" ....

并在该处理程序中应用Jquery插件。

答案 1 :(得分:0)

是的,您可以使用事件在数据存在时发出信号。虽然您可能必须使用$ rootScope,但取决于用例。

但我认为你的错误最可能的原因是,AngularJS首次使用未定义的值初始化它们时会调用所有$ watchers。

你应该检查你的$ watch功能,可以立即解决你的问题。

答案 2 :(得分:0)

正如其他人指出的那样,你需要等到所有的东西都被渲染,这只有在你的api调用返回结果后才会发生。您的指令的信号将是gallery.images数组的更改。所以,让你的指令像下面那样注意它。

请注意新推出的隔离范围 - 对我而言,它听起来应该是孤立的,但我无法确切地说出来,因为我不知道您的其他应用代码。

无论如何,它告诉angular,模板中photoswipe属性中使用的任何值(见下文)都应绑定到指令范围的images属性。使用这样的构造可以确保gallery.images的每次更改都会发生这种情况,无论何时以及如何发生。你需要在外面做的只是在api调用结束时改变集合,剩下的就行了。没有事件(对我来说,它们在角度1.x中非常麻烦),不同组件之间没有连接,没有关注的混合。很干净的解决方案但是,您需要了解$watchCollection()的工作原理,它与常规$watch略有不同,因为它执行浅层集合扫描,不会比较集合本身的对象,只会比较它们的引用。

另一个重要时刻是清理。您必须确保当从集合中删除某些内容时,相应的事件以及插件绑定到元素的任何其他内容都会被正确销毁,否则您将最终出现内存泄漏,意外事件和性能不佳。

App.directive('photoswipe', function ($timeout) {
return {
    replace: false,
    restrict: 'A',
    scope: {
        "images": "=photoswipe"
    },
    link: function photoSwipeLink(scope, element, attr) {
        scope.$watchCollection('images', function(value){
            // here $timeout() is necessary because this event will be fired
            // immediately after collection change
            // thus not giving angular time to render it
            // so, jquery plugin will be fired on the next angular digest
            // and everything will be rendered by then.
            $timeout(function () {
                angular.element('#gallery a').photoSwipe({
                    enableMouseWheel: false,
                    enableKeyboard: false
                });
                // and now figure out how to clean it up!
            });
        });
    }
  };
});

现在到模板。请注意,我第一次使用photoswipe="gallery.images"属性。这就是告诉angular将gallery.images绑定到photoswipe指令到images属性的范围的内容。

由于指令现在引入了自己的隔离范围,因此其中的所有内容都应该考虑到这一事实。这就是为什么ng-repeat="image in images"

<h1>{{ gallery.headline }}</h1>
<ul id="gallery" photoswipe="gallery.images">
  <li class="gallery" ng-repeat="image in images">
    <a href="{{ image.url }}" alt="image.caption">
      <img ng-src="{{ image.thumb_url }}" class="thumb" alt="{{ image.caption }}" style="display: block;">
      <h3 class="headline">{{ image.caption }}</h3>
    </a>
  </li>
</ul>

老实说,我没有测试这段代码,但除了可能的语法错误之外,它应该有效。另外,我想再强调一点,我不知道你的应用程序的其余部分的结构,因此如果你的指令中的某些东西被绑定到某个更高的范围,引入隔离的范围可能会破坏它 - 但对我来说这是个坏主意本身是因为它引入了组件之间隐藏的互连。