AngularJS + JQuery: วิธีรับเนื้อหาแบบไดนามิกที่ทำงานใน angularjs


84

ฉันกำลังทำงานกับแอพ Ajax โดยใช้ทั้ง jQuery และ AngularJS

เมื่อฉันอัปเดตเนื้อหา (ซึ่งมีการโยง AngularJS) ของ div โดยใช้htmlฟังก์ชันของ jQuery การเชื่อม AngularJS จะไม่ทำงาน

ต่อไปนี้เป็นรหัสของสิ่งที่ฉันพยายามทำ:

$(document).ready(function() {
  $("#refreshButton").click(function() {
    $("#dynamicContent").html("<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>")
  });
});
</style><script src="http://docs.angularjs.org/angular-1.0.1.min.js"></script><style>.ng-invalid {
  border: 1px solid red;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.2.23/angular.min.js"></script>
<div ng-app="">
  <div id='dynamicContent'>
    <button ng-click="count = count + 1" ng-init="count=0">
        Increment
      </button>
    <span>count: {{count}} </span>
  </div>


  <button id='refreshButton'>
    Refresh
  </button>
</div>

ฉันมีเนื้อหาแบบไดนามิกภายใน div ที่มี ID #dynamicContentและฉันมีปุ่มรีเฟรชที่จะอัปเดตเนื้อหาของ div นี้เมื่อคลิกรีเฟรช การเพิ่มจะทำงานตามที่คาดไว้หากฉันไม่รีเฟรชเนื้อหา แต่หลังจากรีเฟรชการเชื่อม AngularJS จะหยุดทำงาน

สิ่งนี้อาจไม่ถูกต้องใน AngularJS แต่ในตอนแรกฉันสร้างแอปพลิเคชันด้วย jQuery และเริ่มใช้ AngularJS ในภายหลังดังนั้นฉันจึงไม่สามารถย้ายข้อมูลทั้งหมดไปยัง AngularJS ได้ ความช่วยเหลือใด ๆ ในการทำงานนี้ใน AngularJS ได้รับการชื่นชมอย่างมาก


1
มีเหตุผลพิเศษในการใช้ JQuery สำหรับฟังก์ชันนี้หรือไม่? เนื่องจากสิ่งนี้ครอบคลุมอย่างสวยงามและง่ายดายโดย angular: < jsfiddle.net/pkozlowski_opensource/YCrFD/2 >
pkozlowski.opensource

3
นี่เป็นเพียงกรณีการใช้งานจริงของฉันที่เรียบง่ายเพื่อแสดงปัญหา ในเนื้อหาไดนามิกของแอปพลิเคชันจริงถูกสร้างขึ้นโดย grails taglib ซึ่งส่งต่อไปยัง jquery เป็น html ดังนั้นฉันจึงไม่สามารถพอร์ตลอจิกทั้งหมดใน grails taglib ไปที่ angularjs เพื่อทำให้เป็น angularjs ที่บริสุทธิ์
ราชา

1
@ pkozlowski.opensource ลิงค์นั้นตายแล้ว? นอกจากนี้คุณควรเข้าร่วมoutdoors.stackexchange.com :)
Liam

คำตอบ:


115

คุณต้องเรียก$compileใช้สตริง HTML ก่อนที่จะแทรกลงใน DOM เพื่อให้เชิงมุมได้รับโอกาสในการเชื่อมโยง

ในซอของคุณมันจะเป็นแบบนี้

$("#dynamicContent").html(
  $compile(
    "<button ng-click='count = count + 1' ng-init='count=0'>Increment</button><span>count: {{count}} </span>"
  )(scope)
);

แน่นอน$compileต้องฉีดเข้าไปในคอนโทรลเลอร์ของคุณเพื่อให้สิ่งนี้ทำงานได้

อ่านเพิ่มเติมในเอกสาร$compile


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

1
@Raja เมื่อคุณเรียกเมธอดเชิงมุม / นิพจน์นอกกรอบเชิงมุมคุณควรเรียก$ ขอบเขต $ ใช้ ()เพื่อดำเนินการวงจรชีวิตขอบเขตที่เหมาะสมของการจัดการข้อยกเว้นการดำเนินการนาฬิกา ฯลฯjsfiddle.net/fABdD/14
Artem Andreev

1
@ ArtemAndreev ใช่เลย @Raja คุณสามารถย้ายการโทรไป$scope.$apply()ยังrefresh()ฟังก์ชันได้หากเหมาะสมกับสถาปัตยกรรมของคุณ: jsfiddle.net/nfreitas/8xkeh/1
Noah Freitas

3
ลิงก์ไปยังangular-1.0.1.min.jsในตัวอย่างต้นฉบับคือ 404 ต่อไปนี้เป็นตัวอย่างเวอร์ชันที่ใช้งานได้ของ @ ArtemAndreev-s: jsfiddle.net/pmorch/fABdD/186และ @NoahFreitas ตัวอย่าง: jsfiddle.net/pmorch/8xkeh/43
Peter V. Mørch

1
การใช้ $ คอมไพล์และจัดการ DOM ภายในคอนโทรลเลอร์จะนำคุณไปสู่เส้นทางของการมีโค้ดที่ไม่สามารถทดสอบได้ คำสั่งควรใช้สำหรับการเปลี่ยน DOM ไม่เคยเป็นคอนโทรลเลอร์
Enzey

57

อีกวิธีหนึ่งในกรณีที่คุณไม่สามารถควบคุมเนื้อหาแบบไดนามิกได้

วิธีนี้ใช้ได้ผลถ้าคุณไม่ได้โหลดองค์ประกอบของคุณผ่านคำสั่ง (เช่นในตัวอย่างใน jsfiddles ที่แสดงความคิดเห็น)

สรุปเนื้อหาของคุณ

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

<div class="selector">
    <grid-filter columnname="LastNameFirstName" gridname="HomeGrid"></grid-filter>
</div>

ใช้ Angular Injector

คุณสามารถใช้รหัสต่อไปนี้เพื่ออ้างอิงถึง $ compile หากคุณไม่มี

$(".selector").each(function () {
    var content = $(this);
    angular.element(document).injector().invoke(function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    });
});

สรุป

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

หนึ่งข้อแม้ของรหัสก่อนหน้านี้

หากคุณกำลังใช้บันเดิล asp.net/mvc กับ minify สถานการณ์คุณจะประสบปัญหาเมื่อคุณปรับใช้ในโหมดรีลีส ปัญหามาในรูปแบบของ Uncaught Error: [$ injector: unpr] ซึ่งเกิดจาก minifier ที่ยุ่งกับรหัส javascript เชิงมุม

นี่คือวิธีการแก้ไข :

แทนที่ข้อมูลโค้ดทั่วไปด้วยโอเวอร์โหลดต่อไปนี้

...
angular.element(document).injector().invoke(
[
    "$compile", function($compile) {
        var scope = angular.element(content).scope();
        $compile(content)(scope);
    }
]);
...

สิ่งนี้ทำให้ฉันเสียใจมากก่อนที่จะปะติดปะต่อเข้าด้วยกัน


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

ฉันต้อง $ แยกย่อยหลังจากคอมไพล์
Knu

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

18

เพิ่มเติมจากคำตอบของ @jwize

เนื่องจากangular.element(document).injector()ให้ข้อผิดพลาดinjector is not defined ดังนั้นฉันจึงได้สร้างฟังก์ชันที่คุณสามารถเรียกใช้หลังจากการโทร AJAX หรือเมื่อ DOM ถูกเปลี่ยนโดยใช้ jQuery

  function compileAngularElement( elSelector) {

        var elSelector = (typeof elSelector == 'string') ? elSelector : null ;  
            // The new element to be added
        if (elSelector != null ) {
            var $div = $( elSelector );

                // The parent of the new element
                var $target = $("[ng-app]");

              angular.element($target).injector().invoke(['$compile', function ($compile) {
                        var $scope = angular.element($target).scope();
                        $compile($div)($scope);
                        // Finally, refresh the watch expressions in the new element
                        $scope.$apply();
                    }]);
            }

        }

ใช้มันโดยผ่านตัวเลือกขององค์ประกอบใหม่ แบบนี้

compileAngularElement( '.user' ) ; 

ขอบคุณสิ่งนี้ช่วยชีวิตฉันด้วยแพ็คเกจที่ใช้ UIKit สำหรับกล่องโต้ตอบโดย HTML ที่สร้างขึ้นทันทีภายในฟังก์ชัน $ ขอบเขต ตามหมายเหตุด้านข้างฉันต้องเปลี่ยนตัวเลือกของ $ เป้าหมายให้เหมาะกับความต้องการของฉันในกรณีของฉันคือ $ ["data-ng-controller]") และแสดงความคิดเห็นออก $ scope.apply (); เนื่องจากฟังก์ชันนี้ถูกเรียกใช้แล้วในการเรียกกลับเชิงมุม (ดังนั้นการใช้จึงเกิดขึ้นแล้ว)
zontar

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