มีการโพสต์เรนเดอร์ที่โพสต์สำหรับ Angular JS directive หรือไม่?


139

ฉันเพิ่งได้รับคำสั่งให้ดึงเทมเพลตเพื่อผนวกเข้ากับองค์ประกอบดังนี้:

# CoffeeScript
.directive 'dashboardTable', ->
  controller: lineItemIndexCtrl
  templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
  (scope, element, attrs) ->
    element.parent('table#line_items').dataTable()
    console.log 'Just to make sure this is run'

# HTML
<table id="line_items">
    <tbody dashboard-table>
    </tbody>
</table>

ฉันยังใช้ปลั๊กอิน jQuery ชื่อ DataTables การใช้งานทั่วไปของมันเป็นเช่นนี้: $ ('table # some_id') dataTable () คุณสามารถส่งผ่านข้อมูล JSON ไปยัง dataTable () โทรเพื่อให้ข้อมูลตารางหรือคุณสามารถมีข้อมูลในหน้าแล้วและมันจะทำส่วนที่เหลือ .. ฉันกำลังทำหลัง .. มีแถวอยู่แล้วในหน้า HTML .

แต่ปัญหาคือฉันต้องเรียก dataTable () บนตาราง # line_items หลังจาก DOM พร้อมแล้ว คำสั่งของฉันด้านบนเรียกเมธอด dataTable () ก่อนที่เทมเพลตจะถูกผนวกเข้ากับองค์ประกอบของคำสั่ง มีวิธีที่ฉันสามารถเรียกใช้ฟังก์ชั่นหลังจากผนวก?

ขอขอบคุณสำหรับความช่วยเหลือของคุณ!

อัปเดต 1 หลังจากคำตอบของ Andy:

ฉันต้องการตรวจสอบให้แน่ใจว่าวิธีการเชื่อมโยงเรียกว่าหลังจากทุกอย่างอยู่ในหน้าดังนั้นฉันจึงเปลี่ยนคำสั่งสำหรับการทดสอบเล็กน้อย:

# CoffeeScript
#angular.module(...)
.directive 'dashboardTable', ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.find('#sayboo').html('boo')

      controller: lineItemIndexCtrl
      template: "<div id='sayboo'></div>"

    }

และฉันเห็น "boo" ใน div # sayboo แน่นอน

จากนั้นฉันก็ลองโทรหา jatery Data

.directive 'dashboardTable',  ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        element.parent('table').dataTable() # NEW LINE

      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

ไม่มีโชค

จากนั้นฉันลองเพิ่มเวลา:

.directive 'dashboardTable', ($timeout) ->
    {
      link: (scope,element,attrs) -> 
        console.log 'Just to make sure this gets run'
        $timeout -> # NEW LINE
          element.parent('table').dataTable()
        ,5000
      controller: lineItemIndexCtrl
      templateUrl: "<%= asset_path('angular/templates/line_items/dashboard_rows.html') %>"
    }

และนั่นใช้งานได้ ดังนั้นฉันสงสัยว่าเกิดอะไรขึ้นในโค้ดเวอร์ชันที่ไม่ใช่ตัวจับเวลา?


1
@ การออกแบบไม่ฉันไม่เคยทำฉันต้องใช้ตัวจับเวลา ด้วยเหตุผลบางอย่างการโทรกลับไม่ใช่การติดต่อกลับที่นี่จริงๆ ฉันมีตารางที่มี 11 คอลัมน์และ 100 แถวดังนั้นมุมที่เป็นธรรมชาติจึงเป็นทางเลือกที่ดีที่จะใช้ในการผูกข้อมูล แต่ฉันต้องใช้ jquery Datatables plugin ซึ่งง่ายเหมือน $ ('table'). datatable () ใช้ directive หรือเพียงแค่มีวัตถุ json โง่ ๆ ที่มีแถวทั้งหมดและใช้ ng-repeat เพื่อวนซ้ำฉันไม่สามารถรับ $ () ของฉัน datatable () เพื่อเรียกใช้หลังจากที่องค์ประกอบ html ของตารางแสดงผลดังนั้นตอนนี้เคล็ดลับของฉันคือการจับเวลา เพื่อตรวจสอบว่า $ ('tr'). length> 3 (b / c ของ header / footer)
Nik So

2
@ การออกแบบและใช่ฉันพยายามรวบรวมวิธีทั้งหมดรวบรวมวิธีการคืนวัตถุที่มีวิธีการ postLink / preLink รวบรวมวิธีการส่งกลับเพียงฟังก์ชั่น (คือฟังก์ชั่นการเชื่อมโยง) วิธีการเชื่อมโยง (ไม่มีวิธีการรวบรวมเพราะเท่าที่ฉันสามารถบอกได้ หากคุณมีวิธีการคอมไพล์ที่คืนค่าวิธีการเชื่อมโยงฟังก์ชันการเชื่อมโยงจะถูกละเว้น) .. ไม่มีการทำงานใดที่ต้องพึ่งพาการหมดเวลา $ เก่าที่ดี จะอัปเดตโพสต์นี้หากฉันพบสิ่งใดที่ใช้งานได้ดีขึ้นหรือเพียงแค่พบว่าการติดต่อกลับนั้นทำหน้าที่เหมือนโทรกลับจริงๆ
Nik So

คำตอบ:


215

หากไม่ได้ระบุพารามิเตอร์ที่สอง "การหน่วงเวลา" การทำงานเริ่มต้นคือการใช้งานฟังก์ชันหลังจากที่ DOM แสดงผลเสร็จ ดังนั้นแทนที่จะ setTimeout ใช้ $ timeout:

$timeout(function () {
    //DOM has finished rendering
});

8
ทำไมมันไม่อธิบายในเอกสาร ?
Gaui

23
คุณพูดถูกฉันเข้าใจผิดเพราะฉันพยายามทำให้มันง่าย คำตอบทั้งหมดคือเอฟเฟกต์นี้ไม่ได้เกิดจาก Angular แต่เป็นเบราว์เซอร์ $timeout(fn)ท้ายที่สุดการโทรsetTimeout(fn, 0)ที่มีผลกระทบต่อการเรียกใช้ Javascript และการอนุญาตให้เบราว์เซอร์แสดงเนื้อหาเป็นอันดับแรกก่อนที่จะดำเนินการเรียกใช้ Javascript นั้นต่อไป
รัฐสภา

7
คิดว่าเบราว์เซอร์เป็นการทำงานของคิวบางอย่างเช่น "การประมวลผลจาวาสคริปต์" และ "การสร้างภาพ DOM" แยกจากกันและสิ่งที่ setTimeout (fn, 0) แสดงผลเบราว์เซอร์ที่เรียกใช้งาน "จาวาสคริปต์" ที่ด้านหลังของคิว .
รัฐสภา

2
@GabLeRoux yup ที่จะมีผลเหมือนกันยกเว้น $ หมดเวลามีผลประโยชน์เพิ่มเติมของการโทร $ scope $ Apply () หลังจากที่มันทำงาน ด้วย _.defer () คุณจะต้องเรียกมันด้วยตนเองหาก myFunction เปลี่ยนตัวแปรในขอบเขต
รัฐสภา

2
ฉันมีสถานการณ์ที่สิ่งนี้ไม่ได้ช่วยให้องค์ประกอบของ page1 ng-repeat แสดงซ้ำจากนั้นฉันไปที่ page2 จากนั้นฉันกลับไปที่ page1 และฉันพยายามรับพาเรนต์องค์ประกอบ ng-repeat ที่สูง ... มันคืนความสูงผิด ถ้าฉันหมดเวลาเช่น 1000ms มันก็ใช้ได้
yodalr

14

ฉันมีปัญหาเดียวกันและฉันเชื่อว่าคำตอบนั้นไม่จริง ดูความคิดเห็นของMiškoและบางอภิปรายในกลุ่ม

Angular สามารถติดตามได้ว่าการเรียกฟังก์ชั่นทั้งหมดที่ใช้ในการจัดการ DOM นั้นเสร็จสมบูรณ์แล้ว แต่เนื่องจากฟังก์ชั่นเหล่านี้สามารถกระตุ้นให้เกิด async ตรรกะที่ยังคงอัปเดต DOM หลังจากที่พวกเขากลับมา Angular ไม่สามารถรู้ได้ การโทรกลับเป็นครั้งคราวอาจให้ผลในบางครั้ง แต่ไม่น่าเชื่อถือ

เราแก้ไขฮิวริสติกนี้ด้วย setTimeout อย่างที่คุณทำ

(โปรดทราบว่าทุกคนไม่เห็นด้วยกับฉัน - คุณควรอ่านความคิดเห็นในลิงก์ด้านบนและดูว่าคุณคิดอย่างไร)


7

คุณสามารถใช้ฟังก์ชั่น 'ลิงค์' หรือที่รู้จักในชื่อ postLink ซึ่งทำงานหลังจากใส่เทมเพลต

app.directive('myDirective', function() {
  return {
    link: function(scope, elm, attrs) { /*I run after template is put in */ },
    template: '<b>Hello</b>'
  }
});

ให้สิ่งนี้อ่านถ้าคุณวางแผนที่จะสั่งมันเป็นประโยชน์อย่างมาก: http://docs.angularjs.org/guide/directive


สวัสดีแอนดี้ขอบคุณมากสำหรับการตอบรับ ฉันลองใช้ฟังก์ชั่นลิงค์ แต่ฉันจะไม่ลองอีกครั้ง ฉันใช้เวลา 1.5 วันในการอ่านหน้าคำสั่งนั้น และดูตัวอย่างบนไซต์ของเชิงมุมเช่นกัน จะลองรหัสของคุณตอนนี้
Nik So

อ่าฉันเห็นแล้วว่าคุณกำลังพยายามลิงก์ แต่คุณทำผิดไป หากคุณเพิ่งส่งคืนฟังก์ชันจะถือว่าเป็นลิงก์ หากคุณส่งคืนวัตถุคุณจะต้องส่งคืนพร้อมรหัสเป็นลิงก์ นอกจากนี้คุณยังสามารถส่งคืนฟังก์ชันเชื่อมโยงจากฟังก์ชันคอมไพล์ของคุณได้
Andrew Joslin

สวัสดีแอนดี้ได้ผลลัพธ์ของฉันกลับมา; ฉันเกือบสูญเสียสติเพราะฉันทำตามคำตอบของคุณจริงๆ โปรดดูการอัปเดตของฉัน
Nik So

หืมมลองอะไรเช่น: <table id = "bob"> <tbody dashboard-table = "# bob"> </tbody> </table> จากนั้นในลิงค์ของคุณให้ทำ $ (attrs.dashboardTable) .dataTable () เพื่อ ตรวจสอบให้แน่ใจว่ามันถูกเลือก หรือฉันเดาว่าคุณได้ลองแล้ว .. ฉันไม่แน่ใจว่าลิงก์นั้นใช้งานไม่ได้หรือไม่
Andrew Joslin

อันนี้ใช้งานได้สำหรับฉันฉันต้องการย้ายองค์ประกอบภายในการโพสต์การแสดงผลของแม่แบบสำหรับความต้องการของฉันทำอย่างนั้นในฟังก์ชั่นลิงค์
ขอบคุณ

7

แม้ว่าคำตอบของฉันจะไม่เกี่ยวข้องกับดาต้าดาต้า แต่มันก็จัดการกับปัญหาของการจัดการ DOM และเช่นการเริ่มต้นปลั๊กอิน jQuery สำหรับคำสั่งที่ใช้กับองค์ประกอบที่มีเนื้อหาของพวกเขาปรับปรุงในลักษณะ async

แทนที่จะใช้การหมดเวลาเพียงแค่เพิ่มนาฬิกาที่จะรับฟังการเปลี่ยนแปลงเนื้อหา (หรือทริกเกอร์ภายนอกเพิ่มเติม)

ในกรณีของฉันฉันใช้วิธีแก้ปัญหานี้สำหรับการเริ่มต้นปลั๊กอิน jQuery เมื่อทำซ้ำ ng ซึ่งสร้าง DOM ภายในของฉัน - ในกรณีอื่นฉันใช้มันสำหรับจัดการ DOM หลังจากคุณสมบัติขอบเขตถูกเปลี่ยนแปลงที่คอนโทรลเลอร์ นี่คือวิธีที่ฉันทำ ...

HTML:

<div my-directive my-directive-watch="!!myContent">{{myContent}}</div>

JS:

app.directive('myDirective', [ function(){
    return {
        restrict : 'A',
        scope : {
            myDirectiveWatch : '='
        },
        compile : function(){
            return {
                post : function(scope, element, attributes){

                    scope.$watch('myDirectiveWatch', function(newVal, oldVal){
                        if (newVal !== oldVal) {
                            // Do stuff ...
                        }
                    });

                }
            }
        }
    }
}]);

หมายเหตุ:แทนที่จะเพียงแค่ส่งตัวแปร myContent ไปยัง bool ที่แอ็ตทริบิวต์ my-directive-watch เราสามารถจินตนาการถึงการแสดงออกโดยพลการใด ๆ

หมายเหตุ: การแยกขอบเขตเช่นเดียวกับในตัวอย่างด้านบนสามารถทำได้เพียงครั้งเดียวต่อองค์ประกอบ - การพยายามทำสิ่งนี้กับหลาย ๆ คำสั่งในองค์ประกอบเดียวกันจะส่งผลให้เกิดการคอมไพล์ $ compile: multidir Error - ดู: https://docs.angularjs.org / ข้อผิดพลาด / $ รวบรวม / multidir


7

อาจจะสายเกินไปที่จะตอบคำถามนี้ แต่ก็ยังมีบางคนอาจได้รับประโยชน์จากคำตอบของฉัน

ฉันมีปัญหาที่คล้ายกันและในกรณีของฉันฉันไม่สามารถเปลี่ยนคำสั่งได้เพราะมันเป็นห้องสมุดและการเปลี่ยนรหัสของห้องสมุดไม่ใช่วิธีปฏิบัติที่ดี ดังนั้นสิ่งที่ฉันทำคือใช้ตัวแปรเพื่อรอการโหลดหน้าเว็บและใช้ ng-if ภายใน html ของฉันเพื่อรอการแสดงผลองค์ประกอบเฉพาะ

ในตัวควบคุมของฉัน:

$scope.render=false;

//this will fire after load the the page

angular.element(document).ready(function() {
    $scope.render=true;
});

ใน html ของฉัน (ในส่วนของกรณี html ของฉันคือผ้าใบ)

<canvas ng-if="render"> </canvas>

3

ฉันมีปัญหาเดียวกัน แต่ใช้เชิงมุม + DataTable กับfnDrawCallback+ แถวจัดกลุ่ม + $ สั่งที่ซ้อนกันรวบรวม ฉันวาง $ timeout ในfnDrawCallbackฟังก์ชั่นเพื่อแก้ไขการเรนเดอร์หน้า

ตัวอย่างก่อนตามแหล่งที่มาของแถว row_group:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  for(var i=0; i<nTrs.length; i++){
     //1. group rows per row_grouping example
     //2. $compile html templates to hook datatable into Angular lifecycle
  }
}

หลังจากตัวอย่าง:

var myDrawCallback = function myDrawCallbackFn(oSettings){
  var nTrs = $('table#result>tbody>tr');
  $timeout(function requiredRenderTimeoutDelay(){
    for(var i=0; i<nTrs.length; i++){
       //1. group rows per row_grouping example
       //2. $compile html templates to hook datatable into Angular lifecycle
    }
  ,50); //end $timeout
}

แม้แต่การหมดเวลาสั้น ๆ ก็เพียงพอแล้วที่จะอนุญาตให้ Angular แสดงคำสั่ง Angular ที่รวบรวมไว้ของฉัน


แค่อยากรู้อยากเห็นคุณมีตารางค่อนข้างใหญ่ที่มีหลายคอลัมน์หรือไม่? เพราะฉันพบว่าฉันต้องการจำนวนมิลลิวินาทีที่น่ารำคาญ (> 100) ดังนั้นอย่าปล่อยให้ dataTable () โทรเพื่อทำให้หายใจไม่ออก
Nik So

ฉันพบปัญหาที่เกิดขึ้นจากการนำทางหน้าDataTableสำหรับชุดผลลัพธ์ของ 2 แถวเป็นมากกว่า 150 แถว ดังนั้นไม่ - ฉันไม่คิดว่าขนาดของตารางเป็นปัญหา แต่บางที DataTable ได้เพิ่มค่าการเรนเดอร์ที่เพียงพอในการเคี้ยวบางมิลลิวินาที ฉันมุ่งเน้นไปที่การจัดกลุ่มแถวให้ทำงานใน DataTable ด้วยการรวม AngularJS น้อยที่สุด
JJ Zabkar

2

ไม่มีวิธีใดที่ใช้ได้ผลสำหรับฉันที่ยอมรับจากการหมดเวลาใช้งาน นี่เป็นเพราะฉันใช้เทมเพลตที่ถูกสร้างขึ้นแบบไดนามิกระหว่าง postLink

อย่างไรก็ตามโปรดทราบว่าอาจมีการหมดเวลาเป็น '0' เนื่องจากการหมดเวลาเพิ่มฟังก์ชันที่ถูกเรียกไปยังคิวของเบราว์เซอร์ซึ่งจะเกิดขึ้นหลังจากเอ็นจิ้นการแสดงผลเชิงมุมเนื่องจากนี่อยู่ในคิวแล้ว

อ้างถึงสิ่งนี้: http://blog.brunoscopelliti.com/run-a-directive-after-the-dom-has-finished-rendering


0

นี่คือคำสั่งให้มีการกระทำที่ตั้งโปรแกรมหลังจากเรนเดอร์ตื้น โดยตื้นฉันหมายความว่ามันจะประเมินหลังจากองค์ประกอบที่แสดงมากและที่จะไม่เกี่ยวข้องกับเมื่อเนื้อหาได้รับการแสดงผล ดังนั้นหากคุณต้องการองค์ประกอบย่อยบางส่วนที่ทำการกระทำการโพสต์การแสดงผลคุณควรพิจารณาใช้มันที่นั่น:

define(['angular'], function (angular) {
  'use strict';
  return angular.module('app.common.after-render', [])
    .directive('afterRender', [ '$timeout', function($timeout) {
    var def = {
        restrict : 'A', 
        terminal : true,
        transclude : false,
        link : function(scope, element, attrs) {
            if (attrs) { scope.$eval(attrs.afterRender) }
            scope.$emit('onAfterRender')
        }
    };
    return def;
    }]);
});

จากนั้นคุณสามารถทำได้:

<div after-render></div>

หรือด้วยนิพจน์ที่มีประโยชน์เช่น:

<div after-render="$emit='onAfterThisConcreteThingRendered'"></div>


สิ่งนี้ไม่ได้จริงๆหลังจากแสดงเนื้อหาแล้ว หากฉันมีนิพจน์ภายในองค์ประกอบ <div after-render> {{blah}} </div> ณ จุดนี้การแสดงออกยังไม่ได้รับการประเมิน เนื้อหาของ div ยังคง {{blah}} ภายในฟังก์ชันลิงก์ ดังนั้นในทางเทคนิคคุณจะเริ่มเหตุการณ์ก่อนที่จะแสดงเนื้อหา
Edward Olamisan

นี่คือการกระทำที่ตื้นหลังจากที่ฉันไม่เคยอ้างว่ามันจะลึก
เซบาสเตียน Sastre

0

ฉันได้ทำงานกับคำสั่งต่อไปนี้:

app.directive('datatableSetup', function () {
    return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

และใน HTML:

<table class="table table-hover dataTable dataTable-columnfilter " datatable-setup="">

ปัญหาการยิงถ้าข้างต้นไม่ได้ผลสำหรับคุณ

1) โปรดทราบว่า 'datatableSetup' เทียบเท่ากับ 'datatable-setup' เชิงมุมเปลี่ยนรูปแบบเป็นกรณีอูฐ

2) ตรวจสอบให้แน่ใจว่ามีการกำหนดแอพไว้ก่อนคำสั่ง เช่นนิยามแอพง่าย ๆ และคำสั่ง

var app = angular.module('app', []);
app.directive('datatableSetup', function () {
    return { link: function (scope, elm, attrs) { elm.dataTable(); } }
});

0

ตามความจริงที่ว่าคำสั่งโหลดไม่สามารถคาดการณ์ได้สามารถใช้วิธีแก้ปัญหาง่ายๆ

ลองดูที่คำสั่ง -user ของคำสั่ง โดยทั่วไปผู้ใช้ของคำสั่งจะให้ข้อมูลบางอย่างกับคำสั่งหรือใช้ฟังก์ชั่นบางอย่าง (ฟังก์ชั่น) อุปกรณ์สั่ง คำสั่งในทางกลับกันคาดว่าตัวแปรบางอย่างจะถูกกำหนดในขอบเขตของมัน

หากเราสามารถตรวจสอบให้แน่ใจว่าผู้เล่นทุกคนมีข้อกำหนดการกระทำทั้งหมดของพวกเขาก่อนที่จะพยายามดำเนินการการกระทำเหล่านั้น - ทุกอย่างควรจะดี

และตอนนี้คำสั่ง:

app.directive('aDirective', function () {
    return {
        scope: {
            input: '=',
            control: '='
        },
        link: function (scope, element) {
            function functionThatNeedsInput(){
                //use scope.input here
            }
            if ( scope.input){ //We already have input 
                functionThatNeedsInput();
            } else {
                scope.control.init = functionThatNeedsInput;
            }
          }

        };
})

และตอนนี้ผู้ใช้ของคำสั่ง html

<a-directive control="control" input="input"></a-directive>

และบางแห่งในคอนโทรลเลอร์ของส่วนประกอบที่ใช้คำสั่ง:

$scope.control = {};
...
$scope.input = 'some data could be async';
if ( $scope.control.functionThatNeedsInput){
    $scope.control.functionThatNeedsInput();
}

เกี่ยวกับมัน. มีค่าใช้จ่ายจำนวนมาก แต่คุณสามารถเสียเงิน $ timeout ได้ นอกจากนี้เรายังสมมติว่าองค์ประกอบที่ใช้คำสั่งนั้นอินสแตนซ์ก่อนคำสั่งเพราะเราขึ้นอยู่กับตัวแปรควบคุมที่จะอยู่เมื่อมีคำสั่งอินสแตนซ์

โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.