AngularJS ทำฟังก์ชันลิงก์หลังจากส่งคืนการตอบกลับ $http

ฉันจำเป็นต้องใช้ฟังก์ชันลิงก์ในคำสั่งหลังจากการตอบกลับ http แนวคิดเป็นดังนี้:

<input type="text" my-field>
<script>
 angular.module("mine")
 .controller ('myCtrl', function ($scope) {
  $http.get("/my/service").success(function (data, status, headers, config) {
    // OK, done with the query... now I know my field name to bind to. Somehow
    // I have to get it down to the link function below...
  });
 })
 .directive ('myField', function ($compile) {
  return {
    link: function (scope, element, attrs) {
      var my_field = attrs.myField;
      element.removeAttr('my-field');

      // Somehow figure out the field here in ngFieldSpec
      element.attr('ng-model', ngFieldSpec);
      $compile(element)(scope);
    };
   });
</script>

ที่นี่ ฉันต้องผูกฟิลด์อินพุตเข้ากับองค์ประกอบของการตอบกลับ แต่ฉันไม่รู้ว่าองค์ประกอบนั้นจะถูกเรียกว่าอะไรจนกว่าฉันจะได้รับคำตอบ แต่เมื่อฉันรันมัน ลิงก์ของคำสั่งจะทำงานก่อนที่ $http จะเสร็จสิ้น: ลำดับที่แท้จริงคือ

  • $http.get เริ่มต้น
  • เรียกใช้ฟังก์ชันลิงก์ของคำสั่ง
  • $http.get คืนความสำเร็จ

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

แก้ไขเพื่อเพิ่มข้อมูลเพิ่มเติมเพื่อตอบสนองต่อคำขอ:

ฉันมีบริการที่ส่งคืนโครงสร้างข้อมูล JSON ฉันไม่ทราบล่วงหน้าแน่ชัดว่าโครงสร้างข้อมูลนั้นจะมีลักษณะอย่างไร แต่ฉันสามารถเข้าใจและจับคู่ฟิลด์กับฟิลด์อินพุตของเพจของฉันได้ ฉันกำลังพยายามจับคู่สิ่งนี้ในฟังก์ชันลิงก์ ฉันดีใจที่ได้ทำที่อื่น ฉันสามารถทำได้ในฟังก์ชัน $http.success แต่นั่นจะเป็นการดำเนินการจัดการ DOM ในคอนโทรลเลอร์ และความเข้าใจของฉันก็คือการจัดการ DOM ควรทำในคำสั่งเท่านั้น

HTML ของฉันต้องมีลักษณะดังนี้:

<input type="text" my-field="[MY_EXTENSION_NAME]myFieldName">
<input type="text" my-field="[MY_EXTENSION_NAME]myFieldName2">
<input type="text" my-field="[MY_EXTENSION_NAME_2]myFieldName">

การตอบกลับจากเซิร์ฟเวอร์จะเป็นดังนี้:

{
    realField1: "Diddly",
    realField2: "Squat",
    extensions: [
      {
        name: "MY_EXTENSION_NAME",
        fields: [
          { name="myFieldName" value="Foo" },
          { name="myFieldName2" value="Bar" }
        ]
      },
      {
        name: "MY_EXTENSION_NAME_2",
        fields: [
          { name="myFieldName" value="Baz" },
          { name="myFieldName2" value="Buz" }
        ]
      }
    ]
 }

การตอบสนองของเซิร์ฟเวอร์อาจแตกต่างกันเนื่องจาก:

  • อาจมีส่วนขยายจำนวนเท่าใดก็ได้ ("MY_EXTENSION_NAME" ฯลฯ)
  • ส่วนขยายอาจส่งคืนในลำดับใดก็ได้
  • อาจมีฟิลด์จำนวนเท่าใดก็ได้
  • ฟิลด์อาจถูกส่งคืนในลำดับใดก็ได้

ปัญหาทั้งหมดที่นี่คือฉันต้องการแปลง "[MY_EXTENSION_NAME]myFieldName" เป็น ng-model "model.extensions[0].fields[0].value อย่างไรก็ตาม ตอนนี้ฉันกำลังคิดที่จะแปลงข้อมูลเป็นรูปแบบมาตรฐานในระหว่างการอ่าน จะง่ายกว่า ดังนั้น ng-model จึงสามารถเป็น "model.my_extension_name.myFieldName" ได้


person fool4jesus    schedule 11.01.2014    source แหล่งที่มา
comment
ไม่แน่ใจ แต่ฉันคิดว่า $watch สามารถช่วยได้   -  person Beterraba    schedule 11.01.2014
comment
เหตุใดคุณจึงใช้ $compile ภายในฟังก์ชันลิงก์ คุณอาจสามารถบรรลุสิ่งที่คุณต้องการได้ด้วยขอบเขตที่แยกได้โดยใช้ '@' โปรดอธิบายสิ่งที่คุณต้องการทำ   -  person Ilan Frumer    schedule 11.01.2014
comment
ขออภัย ฉันคิดว่าฉันได้อธิบายสิ่งที่ฉันต้องการจะทำแล้ว ฉันจะอัปเดต   -  person fool4jesus    schedule 11.01.2014
comment
อย่างไรก็ตาม ฉันกำลังเรียก $compile in link เพราะมีคนแนะนำใน SO อื่นและใช้งานได้ แต่ฉันยินดีที่จะมีวิธีที่ดีกว่าในการทำเช่นนั้นด้วย   -  person fool4jesus    schedule 12.01.2014


คำตอบ (1)


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

1.
กำหนดสัญญาในขอบเขตของคุณ:

app.controller('myCtrl', function ($http, $scope) {
    $scope.model = {
        promise: $http.get('/my/service'),
        myField01: 'Hello, world, from 01 !',
        myField02: 'Hello, world, from 02 !',
        myField03: 'Hello, world, form 03 !'
    };
});

2.
จาก HTML ของคุณ ให้อ้างอิงถึงคำมั่นสัญญาเพื่อส่งต่อไปยังคำสั่งของคุณ:

<input type="text" my-field="model.promise" />

3.
นำคำมั่นสัญญานี้ไปไว้ในขอบเขตแยกของคำสั่งของคุณ:

app.directive ('myField', function ($compile) {
    return {
        scope: { promise: '=myField' },
        ...

4.
ในฟังก์ชัน link ของคุณ ให้ลงทะเบียนการติดต่อกลับเมื่อสัญญาได้รับการแก้ไข (เช่น คุณได้รับการตอบกลับคำขอของคุณ) และดำเนินการจัดการที่จำเป็นทั้งหมด:

...
link: function (scope, elem, attrs) {
    scope.promise.success(function (data) {
        elem.removeAttr('my-field');
        elem.attr('ng-model', 'model.' + data.fieldName);
        $compile(elem)(scope.$parent);
    });
}

โปรดดูการสาธิตแบบสั้นนี้ด้วย

person gkalpak    schedule 11.01.2014
comment
ขอบคุณสำหรับข้อเสนอแนะที่เป็นรูปธรรม ในภาพรวม ฉันยังใหม่กับ Angular และฉันยินดีที่จะรับข้อเสนอแนะอื่น อะไรจะดีไปกว่านั้น? ก่อนหน้านี้ฉันแก้ไขคำถามเพื่อให้ชัดเจนว่าฉันกำลังพยายามทำอะไร - ฉันต้องผูกช่องป้อนข้อมูลบางช่องกับข้อมูล แต่ฉันไม่รู้ว่าช่องข้อมูลนั้นเรียกว่าอะไรจนกระทั่ง $http.get ประสบความสำเร็จ ไม่รู้จะอธิบายยังไงให้ชัดเจนกว่านี้ - person fool4jesus; 12.01.2014
comment
สิ่งที่ฉันหมายถึงคือมันไม่ชัดเจนว่าคุณกำลังพยายามบรรลุอะไรในระดับที่สูงขึ้น เช่น. ทำไมคุณต้องมีช่องป้อนข้อมูลที่คุณไม่รู้ว่าจะผูกกับอะไร ฯลฯ - person gkalpak; 12.01.2014
comment
ขอโทษ. เนื่องจากเซิร์ฟเวอร์อาจส่งคืนข้อมูลที่มีโครงสร้างในรูปแบบที่แตกต่างกัน มีลำดับชั้นสองระดับ และแต่ละระดับของลำดับชั้นจะมีบางอย่างเช่น ‹item›‹name›foo‹/name›‹value›bar‹/value›‹/item› ทั้งหมดที่ฉันรู้ในแอตทริบิวต์คือ foo.bar... เพื่อที่จะแมปกับแอตทริบิวต์ ng-model เช่น (พูด) model.extensions[3].fields[4].value อีกวิธีหนึ่งคือการแมปข้อมูลใหม่เป็นแฮชสองระดับ แล้วกลับมาอีกครั้งเมื่อบันทึก แต่ฉันไม่คิดว่าจะช่วยได้จริงๆ เพราะเมื่อมีการเรียกคำสั่ง ส่วนขยาย[3] ก็จะยังไม่มีอยู่ ข้อเสนอแนะยินดีแม้ว่า :-) - person fool4jesus; 13.01.2014
comment
แทนที่จะมีองค์ประกอบ DOM บางอย่างคุณไม่รู้ว่าจะผูกกับอะไร (จึงต้องรอการตอบกลับจากเซิร์ฟเวอร์เพื่อบอกคุณว่าจะผูกแต่ละองค์ประกอบกับอะไร) ทำไมไม่รับข้อมูลจากเซิร์ฟเวอร์และ จากนั้น แทรกองค์ประกอบ DOM ที่เหมาะสม (ผูกไว้กับค่าที่เหมาะสม) ? (ขออภัย หากคำแนะนำของฉันไม่สมเหตุสมผล ฉันต้องยอมรับว่าฉันยังนึกภาพการตั้งค่าของคุณไม่ได้ 100%) - person gkalpak; 13.01.2014
comment
ขอบคุณที่อยู่กับฉัน - ความคิดที่น่าสนใจ ประเด็นก็คือผู้เขียน HTML เขียนองค์ประกอบ DOM ลงในเทมเพลต Angular และเขารู้ว่าเซิร์ฟเวอร์จะกลับมาตอนรันไทม์อะไร เขาแค่ไม่รู้รูปแบบข้อมูลที่แน่นอน ความเป็นไปได้ประการหนึ่งคือการให้ผู้เขียนเขียนองค์ประกอบ แต่ไม่สร้างคำสั่งในฟิลด์เลย แต่เป็นเพียงแอตทริบิวต์ปกติ หลังจากที่เราอ่านจากเซิร์ฟเวอร์แล้ว ให้ไปที่ฟิลด์ต่างๆ สร้างแอตทริบิวต์ ng-model และ $compile นั่นหมายถึงการจัดการ DOM ในคอนโทรลเลอร์ ถึงกระนั้นมันก็ค่อนข้างจะน้อย - person fool4jesus; 13.01.2014
comment
มันแย่ลงเรื่อยๆ (ฉันสับสน) ! บางทีการเห็นโค้ดบางส่วนอาจช่วยทำให้สิ่งต่าง ๆ ชัดเจนขึ้น (ถ้าคุณไม่รังเกียจที่จะโพสต์ส่วนที่เกี่ยวข้องบางส่วน) - person gkalpak; 13.01.2014
comment
แน่นอนว่าฉันจะแก้ไขคำถามเดิมของฉัน BTW ฉันทำให้มันใช้งานได้โดยแปลงการตอบสนองของเซิร์ฟเวอร์เป็นรูปแบบมาตรฐานแล้วอ้างอิงสิ่งนั้น - person fool4jesus; 13.01.2014
comment
ตกลง ฉันเข้าใจแล้วตอนนี้ ฉันเห็นด้วยกับความคิดเห็นล่าสุดของคุณ (เกี่ยวกับการแปลงการตอบกลับเพื่อทำให้โมเดลง่ายขึ้น) เป็นวิธีแก้ปัญหาที่สะอาดที่สุด (และยังคงแยกข้อกังวลระหว่างผู้ควบคุม คำสั่ง มุมมอง ฯลฯ) ทางออกเดียวที่ฉันต้องการดีกว่าคือถ้าคุณสามารถให้เซิร์ฟเวอร์ส่งข้อมูลในรูปแบบมาตรฐานได้ตั้งแต่แรก - person gkalpak; 13.01.2014
comment
ใช่แล้ว ฉันทำอะไรไม่ได้มากเกี่ยวกับเรื่องนั้น :-) ขอบคุณสำหรับการสนทนา - person fool4jesus; 22.01.2014