การตั้งค่าตัวแปรขอบเขตไดนามิกใน AngularJs - ขอบเขต <some_string>


97

ฉันมีสตริงที่ได้รับจาก a routeParamหรือคุณลักษณะคำสั่งหรืออะไรก็ตามและฉันต้องการสร้างตัวแปรตามขอบเขตตามนี้ ดังนั้น:

$scope.<the_string> = "something".

อย่างไรก็ตามหากสตริงมีจุดอย่างน้อยหนึ่งจุดฉันต้องการแยกและ "เจาะลึก" ลงในขอบเขต ดังนั้นควรจะเป็น'foo.bar' $scope.foo.barซึ่งหมายความว่าเวอร์ชันธรรมดาจะไม่ทำงาน!

// This will not work as assigning variables like this will not "drill down"
// It will assign to a variables named the exact string, dots and all.
var the_string = 'life.meaning';
$scope[the_string] = 42;
console.log($scope.life.meaning);  // <-- Nope! This is undefined.
console.log($scope['life.meaning']);  // <-- It is in here instead!

เมื่ออ่านตัวแปรตามสตริงคุณจะได้รับพฤติกรรมนี้โดยการทำ$scope.$eval(the_string)แต่จะทำอย่างไรเมื่อกำหนดค่า?

คำตอบ:


174

วิธีการแก้ปัญหาที่ผมพบคือการใช้$ แจง

"แปลงนิพจน์เชิงมุมเป็นฟังก์ชัน"

หากใครมีคำตอบที่ดีกว่าโปรดเพิ่มคำตอบใหม่สำหรับคำถาม!

นี่คือตัวอย่าง:

var the_string = 'life.meaning';

// Get the model
var model = $parse(the_string);

// Assigns a value to it
model.assign($scope, 42);

// Apply it to the scope
// $scope.$apply(); <- According to comments, this is no longer needed

console.log($scope.life.meaning);  // logs 42

1
สวยขนาดนี้! แต่ (และเกือบตลอดเวลา) ฉันไม่สามารถรับ $ ขอบเขต $ ใช้ () เพื่อเผยแพร่ข้อมูลของฉัน ฉันสามารถดูได้ในบันทึกคอนโซล แต่มุมมองของฉันไม่แสดงข้อมูลที่เพิ่มเข้ามาใหม่ (ฉันมีแถบส่วนท้ายที่ฉันแสดง $ ขอบเขต) - มีแนวคิดใด
william e schroeder

ยากที่จะรู้โดยไม่ต้องเห็นรหัส แต่สิ่งแรกที่ฉันคาดเดาคือส่วนท้ายไม่อยู่ในขอบเขตเดียวกัน หากส่วนท้ายอยู่นอกองค์ประกอบที่มี ng-controller = "YourController" ก็จะไม่สามารถเข้าถึงตัวแปรได้
Erik Honn

3
ขอบคุณสำหรับคำตอบ. ฉันอยากจะชี้ให้เห็นว่าการมอบหมายยังใช้ได้กับวัตถุที่ซับซ้อนไม่ใช่แค่ประเภทดั้งเดิม
Patrick Salami

1
เหตุใด $ scope. $ จึงจำเป็น?
sinθ

ฉันจำไม่ได้จริงๆ ฉันคิดว่าเป็นเพราะ model.assign ไม่ทริกเกอร์การผูกข้อมูลดังนั้นคุณต้องสมัครด้วยตนเองเมื่อคุณทำทุกอย่างที่ต้องทำเสร็จแล้ว ลองใช้และดูว่าสามารถลบออกได้หรือไม่ (อาจมีการเปลี่ยนแปลงใน Angular เวอร์ชันที่ใหม่กว่าด้วยฉันคิดว่านี่เป็นเวอร์ชันหนึ่งหรือสองเวอร์ชัน)
Erik Honn

20

ใช้คำตอบของ Erik เป็นจุดเริ่มต้น ฉันพบวิธีแก้ปัญหาที่ง่ายกว่าซึ่งเหมาะกับฉัน

ในฟังก์ชัน ng-click ของฉันฉันมี:

var the_string = 'lifeMeaning';
if ($scope[the_string] === undefined) {
   //Valid in my application for first usage
   $scope[the_string] = true;
} else {
   $scope[the_string] = !$scope[the_string];
}
//$scope.$apply

ฉันได้ทดสอบโดยมีและไม่มีขอบเขต $ ใช้ $ ทำงานได้อย่างถูกต้องหากไม่มี!


13
โปรดทราบว่านี่เป็นสิ่งที่แตกต่างอย่างสิ้นเชิงกับคำถามเกี่ยวกับ เป้าหมายของคำถามคือการสร้างตัวแปรขอบเขตแบบไดนามิกที่มีความลึกมากกว่า 1 ตามสตริง ดังนั้นเพื่อสร้าง "scope.life.meaning" ไม่ใช่ "scope.lifeMeaning" การใช้ $ scope [the_string] จะไม่ทำเช่นนั้น และสำหรับกรณีเฉพาะของคุณโปรดจำไว้ว่า! undefined เป็นจริงใน javascrip ดังนั้นคุณสามารถข้ามคำสั่ง if ทั้งหมดและทำ $ scope [the_string] =! $ scope [the_string]; อาจทำให้โค้ดสับสนเล็กน้อยดังนั้นฉันไม่แน่ใจว่าเป็นสิ่งที่คุณต้องการจริงๆหรือไม่
Erik Honn

@ErikHonn จริง $ ขอบเขต [life.meaning] = จริงสำหรับฉัน!
Jesse P Francis

1
มัน "ใช้งานได้" แต่ถ้าไม่มีอะไรเปลี่ยนแปลงใน 1.6 มันจะไม่ทำแบบเดียวกับที่คำถามถามหา หากคุณทำ $ scope [life.meaning] = true ชื่อของคุณสมบัติจะเป็น "life.meaning" และนั่นไม่ใช่สิ่งที่เราต้องการ เราต้องการทรัพย์สินที่เรียกว่าชีวิตซึ่งมีทรัพย์สินลูกเรียกความหมาย
Erik Honn

13

สร้างตัวแปรเชิงมุมแบบไดนามิกจากผลลัพธ์

angular.forEach(results, function (value, key) {          
  if (key != null) {                       
    $parse(key).assign($scope, value);                                
  }          
});

ปล. อย่าลืมส่งแอตทริบิวต์ $ parse ไปยังฟังก์ชันของคอนโทรลเลอร์ของคุณ


5

หากคุณพอใจกับการใช้ Lodash คุณสามารถทำสิ่งที่ต้องการในบรรทัดเดียวโดยใช้ _.set ():

_.set (วัตถุเส้นทางค่า) ตั้งค่าคุณสมบัติของเส้นทางบนวัตถุ หากไม่มีบางส่วนของเส้นทางจะถูกสร้างขึ้น

https://lodash.com/docs#set

ดังนั้นตัวอย่างของคุณก็จะเป็น: _.set($scope, the_string, something);


4

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

HTML:

<div id="div{{$index+1}}" data-ng-show="val{{$index}}">

$indexดัชนีลูปอยู่ที่ไหน

Javascript ( valueพารามิเตอร์ที่ส่งผ่านไปยังฟังก์ชันอยู่ที่ไหนและจะเป็นค่าของ$indexดัชนีลูปปัจจุบัน):

var variable = "val"+value;
if ($scope[variable] === undefined)
{
    $scope[variable] = true;
}else {
    $scope[variable] = !$scope[variable];
}

สิ่งนี้ไม่เกี่ยวข้องกับคำถามโปรดอยู่ในหัวข้อต่อไป
Erik Honn

ขออภัยคิดว่าอาจช่วยในการสร้างตัวแปรไดนามิกโดยใช้สตริง เธรดนี้ช่วยฉันในการเขียนโค้ดด้านบนซึ่งใช้งานได้ดีสำหรับฉัน
Riaz

ไม่ต้องกังวลการต้องการให้คำตอบเป็นการเริ่มต้นที่ดีเพียงแค่อ่านคำถามก่อน :) ในกรณีนี้มันเกี่ยวกับการแยกสตริง "abc" ที่ไม่รู้จักไปยังวัตถุ {a: {b: {c: ... } }} แต่การจัดการรายการที่ทำซ้ำเป็นปัญหาที่พบบ่อยดังนั้นฉันดีใจที่คุณพบสิ่งที่รักษาได้ในเธรด
Erik Honn

อ้อและตามที่ฉันสังเกตคำตอบอื่นถ้าค่าควรเป็นจริง / เท็จ (หรือไม่ได้กำหนด) คุณสามารถข้ามตรรกะทั้งหมดและทำ $ scope [ตัวแปร] ===! $ scope [ตัวแปร] ได้ตั้งแต่ ! undefined เป็นจริงในจาวาสคริปต์ แม้ว่าคุณจะใช้ typescript ฉันสงสัยว่ามันจะทำให้คุณโกรธ!
Erik Honn

3

โปรดทราบว่านี่เป็นเพียง JavaScript และไม่มีส่วนเกี่ยวข้องกับ Angular JS อย่าสับสนกับสัญลักษณ์ '$' ที่มีมนต์ขลัง;)

ปัญหาหลักคือนี่เป็นโครงสร้างแบบลำดับชั้น

console.log($scope.life.meaning);  // <-- Nope! This is undefined.
=> a.b.c

ไม่ได้กำหนดไว้เนื่องจากไม่มี "$ scope.life" แต่คำข้างต้นต้องการแก้ "ความหมาย"

วิธีแก้ปัญหาควรเป็น

var the_string = 'lifeMeaning';
$scope[the_string] = 42;
console.log($scope.lifeMeaning);
console.log($scope['lifeMeaning']);

หรือมีประสิทธิภาพมากขึ้นเล็กน้อย

var the_string_level_one = 'life';
var the_string_level_two = the_string_level_one + '.meaning';
$scope[the_string_level_two ] = 42;
console.log($scope.life.meaning);
console.log($scope['the_string_level_two ']);

เนื่องจากคุณสามารถเข้าถึงวัตถุโครงสร้างด้วย

var a = {};
a.b = "ab";
console.log(a.b === a['b']);

มีบทช่วยสอนที่ดีมากมายเกี่ยวกับเรื่องนี้ซึ่งจะแนะนำคุณได้ดีตลอดการใช้งาน JavaScript

มีบางอย่างเกี่ยวกับไฟล์

$scope.$apply();
do...somthing...bla...bla

ไปและค้นหาในเว็บสำหรับ 'angular $ apply' และคุณจะพบข้อมูลเกี่ยวกับฟังก์ชัน $ apply และคุณควรใช้วิธีนี้อย่างชาญฉลาดมากขึ้น (หากคุณไม่ได้ใช้เฟส $ สมัคร)

$scope.$apply(function (){
    do...somthing...bla...bla
})

3
ขอบคุณสำหรับคำตอบ แต่คำถามไม่ใช่ว่าทำไม "เวอร์ชันธรรมดา" จึงใช้งานไม่ได้ซึ่งเป็นที่ทราบกันดีตั้งแต่เริ่มต้น คำถามคือทางเลือกคืออะไร ฉันกลัวว่าคำแนะนำทั้งสองของคุณจะไม่ได้ผลทั้งสองข้อต้องการให้คุณรู้โครงสร้างของสตริงล่วงหน้าและประเด็นก็คือเพื่อให้เป็นแบบไดนามิก (และสามารถจัดการกับสตริงได้ตามอำเภอใจ) ดูคำตอบที่ยอมรับสำหรับวิธีแก้ปัญหา
Erik Honn

1
นอกจากนี้คำตอบยังไม่สามารถแก้ปัญหาได้จริง ข้อแรกล้มเหลวในข้อกำหนดเบื้องต้นพื้นฐานที่เราควรกำหนดให้กับ $ scope.abc ไม่ใช่ $ scope ['abc'] และอันที่สองก็ล้มเหลวเช่นกันและส่งผลให้เกิดสิ่งเดียวกับ "เวอร์ชันธรรมดา" จาก คำถามกับไวยากรณ์ที่ไม่จำเป็น แต่บางทีนั่นอาจเป็นการพิมพ์ผิด? :)
Erik Honn

0

หากคุณใช้ไลบรารี Lodash ด้านล่างนี้เป็นวิธีตั้งค่าตัวแปรไดนามิกในขอบเขตเชิงมุม

เพื่อตั้งค่าในขอบเขตเชิงมุม

_.set($scope, the_string, 'life.meaning')

เพื่อรับค่าจากขอบเขตเชิงมุม

_.get($scope, 'life.meaning')

-1

หากคุณกำลังพยายามทำในสิ่งที่ฉันคิดว่าคุณกำลังพยายามทำอยู่คุณจะต้องปฏิบัติต่อขอบเขตเหมือนออบเจ็กต์ JS ทั่วไปเท่านั้น

นี่คือสิ่งที่ฉันใช้สำหรับการตอบสนองความสำเร็จของ API สำหรับอาร์เรย์ข้อมูล JSON ...

function(data){

    $scope.subjects = [];

    $.each(data, function(i,subject){
        //Store array of data types
        $scope.subjects.push(subject.name);

        //Split data in to arrays
        $scope[subject.name] = subject.data;
    });
}

ตอนนี้ {{subject}} จะส่งคืนอาร์เรย์ของชื่อหัวเรื่องข้อมูลและในตัวอย่างของฉันจะมีแอตทริบิวต์ขอบเขตสำหรับ {{job}}, {{customers}}, {{staff}} และอื่น ๆ จาก $ scope งาน, $ scope.customers, $ scope.staff


4
ขอบคุณสำหรับคำตอบ แต่นั่นไม่ใช่สิ่งเดียวกัน เป็นเรื่องเกี่ยวกับการสร้าง $ scope.subject.name จากสตริงโดยไม่ต้องฮาร์ดโค้ด $ scope.subject สิ่งนี้มีความเกี่ยวข้องในคำสั่งบางประการที่คุณไม่ควรคิดอะไรเกี่ยวกับขอบเขตหากคุณต้องการให้คำสั่งนั้นสามารถนำกลับมาใช้ใหม่ได้ นอกจากนี้ angular.forEach ()! อย่าปล่อยให้ jQuery
ขัดขวางการ
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.