ตัวดำเนินการเชิงตรรกะในเงื่อนไข handlebars.js {{#if}}


479

มีวิธีใดบ้างในแฮนด์บาร์ JS เพื่อรวมตัวดำเนินการเชิงตรรกะเข้ากับตัวดำเนินการตามเงื่อนไขมาตรฐานของแฮนด์บาร์? บางสิ่งเช่นนี้

{{#if section1 || section2}}
.. content
{{/if}}

ฉันรู้ว่าฉันสามารถเขียนผู้ช่วยของตัวเองได้ แต่ก่อนอื่นฉันต้องแน่ใจว่าฉันไม่ได้คิดค้นล้อใหม่

คำตอบ:


518

สิ่งนี้เป็นไปได้โดย 'การโกง' ด้วยตัวช่วยบล็อก สิ่งนี้อาจขัดกับอุดมการณ์ของคนที่พัฒนาแฮนด์บาร์

Handlebars.registerHelper('ifCond', function(v1, v2, options) {
  if(v1 === v2) {
    return options.fn(this);
  }
  return options.inverse(this);
});

จากนั้นคุณสามารถโทรหาผู้ช่วยในเทมเพลตเช่นนี้

{{#ifCond v1 v2}}
    {{v1}} is equal to {{v2}}
{{else}}
    {{v1}} is not equal to {{v2}}
{{/ifCond}}

54
มันขัดกับธรรมชาติของ Handlebars / Mustache แต่ไร้ประโยชน์ แต่ก็มีประโยชน์แน่นอน!
Bala Clark

6
โปรดทราบว่าสิ่งนี้ไม่สามารถใช้งานได้กับคุณสมบัติที่ถูกผูกไว้ (การอ่าน, การโยง Ember) ใช้ได้ดีกับค่าตามตัวอักษร แต่ไม่สามารถแก้ไขคุณสมบัติของโมเดล (ทดสอบด้วย Ember 1.0.0-rc.8 และ Handlebars 1.0.0) และregisterBoundHelperไม่สามารถจัดการกับไวยากรณ์ของแฮนด์บาร์ วิธีแก้ไขคือการสร้างมุมมองที่กำหนดเอง: stackoverflow.com/questions/18005111/…
Warren Seine

28
@BalaClark มันไร้เหตุผลจริงๆเหรอ? ฉันหมายความว่ามันยังคงมีคำสั่ง "ถ้า" - เพียงเพราะพวกเขาเป็นคนพิการโดยเจตนาไม่เปลี่ยนปรัชญา
phreakhead

111
ฉันไม่เคยเข้าใจเลยว่าทำไมนักพัฒนาถึงชอบแฮนดิแคป
StackOverflow

36
ฉันไม่เข้าใจว่าทำไมและ / หรือเป็นส่วนหนึ่งของแฮนด์บาร์ไม่ได้ มันไม่ได้ขัดแย้งกับธรรมชาติที่ไร้เหตุผลเพราะมีอยู่แล้วถ้า IF, UNLESS และ EACH ... มากสำหรับไร้เหตุผล
Asaf

445

การแก้ปัญหาไปอีกขั้นหนึ่ง เพิ่มตัวดำเนินการเปรียบเทียบ

Handlebars.registerHelper('ifCond', function (v1, operator, v2, options) {

    switch (operator) {
        case '==':
            return (v1 == v2) ? options.fn(this) : options.inverse(this);
        case '===':
            return (v1 === v2) ? options.fn(this) : options.inverse(this);
        case '!=':
            return (v1 != v2) ? options.fn(this) : options.inverse(this);
        case '!==':
            return (v1 !== v2) ? options.fn(this) : options.inverse(this);
        case '<':
            return (v1 < v2) ? options.fn(this) : options.inverse(this);
        case '<=':
            return (v1 <= v2) ? options.fn(this) : options.inverse(this);
        case '>':
            return (v1 > v2) ? options.fn(this) : options.inverse(this);
        case '>=':
            return (v1 >= v2) ? options.fn(this) : options.inverse(this);
        case '&&':
            return (v1 && v2) ? options.fn(this) : options.inverse(this);
        case '||':
            return (v1 || v2) ? options.fn(this) : options.inverse(this);
        default:
            return options.inverse(this);
    }
});

ใช้ในเทมเพลตเช่นนี้:

{{#ifCond var1 '==' var2}}

รุ่น Coffee Script

Handlebars.registerHelper 'ifCond', (v1, operator, v2, options) ->
    switch operator
        when '==', '===', 'is'
            return if v1 is v2 then options.fn this else options.inverse this
        when '!=', '!=='
            return if v1 != v2 then options.fn this else options.inverse this
        when '<'
            return if v1 < v2 then options.fn this else options.inverse this
        when '<='
            return if v1 <= v2 then options.fn this else options.inverse this
        when '>'
            return if v1 > v2 then options.fn this else options.inverse this
        when '>='
            return if v1 >= v2 then options.fn this else options.inverse this
        when '&&', 'and'
            return if v1 and v2 then options.fn this else options.inverse this
        when '||', 'or'
            return if v1 or v2 then options.fn this else options.inverse this
        else
            return options.inverse this

20
อย่าลืมเกี่ยวกับและ'||' '&&'ฉันได้เพิ่มกรณีเหล่านี้และพวกเขามีประโยชน์มาก
Jason

20
การเป็น noob ให้กับแฮนด์บาร์สิ่งหนึ่งที่ไม่ชัดเจนคือคุณต้องผ่านโอเปอเรเตอร์เป็นสตริงมิฉะนั้นคอมไพเลอร์จะผิดพลาดออกไปในขณะที่โทเค็นแม่แบบของคุณ {{#ifCond จริง '==' false}}
Joe Holloway

2
ฉันพบค่าที่ไม่ได้รับการประเมินสำหรับคุณลักษณะของวัตถุ การเพิ่มความช่วยเหลือต่อไปนี้ v1 = Ember.Handlebars.get(this, v1, options) v2 = Ember.Handlebars.get(this, v2, options)
ZX12R

4
แต่ถ้า v1 และ v2 เป็นเงื่อนไขเดียวฉันจะใช้ได้อย่างไร สำหรับตัวอย่าง: if (value == "a" || value == "b")
กฤษณะ

3
คุณสามารถทำสิ่งอื่นได้หากใช้สิ่งนี้?
jbyrd

160

แฮนด์บาร์รองรับการทำงานที่ซ้อนกัน สิ่งนี้ให้ความยืดหยุ่นมากมาย (และรหัสทำความสะอาด) ถ้าเราเขียนตรรกะของเราแตกต่างกันเล็กน้อย

{{#if (or section1 section2)}}
.. content
{{/if}}

ในความเป็นจริงเราสามารถเพิ่มตรรกะได้ทุกประเภท:

{{#if (or 
        (eq section1 "foo")
        (ne section2 "bar"))}}
.. content
{{/if}}

เพียงลงทะเบียนผู้ช่วยเหลือเหล่านี้:

Handlebars.registerHelper({
    eq: (v1, v2) => v1 === v2,
    ne: (v1, v2) => v1 !== v2,
    lt: (v1, v2) => v1 < v2,
    gt: (v1, v2) => v1 > v2,
    lte: (v1, v2) => v1 <= v2,
    gte: (v1, v2) => v1 >= v2,
    and() {
        return Array.prototype.every.call(arguments, Boolean);
    },
    or() {
        return Array.prototype.slice.call(arguments, 0, -1).some(Boolean);
    }
});

5
โปรดทราบว่านี่เป็นไปไม่ได้จนกว่า HTMLBars ใน Ember 1.10 นอกจากนี้ผู้ช่วยเหลือผู้ที่มาเป็น addon Ember CLI หากคุณต้องการ: github.com/jmurphyau/ember-truth-helpers
stephen.hanson

1
วิธีนี้โดยไม่มีoptionsการดำเนินการจะดีกว่าที่จะดำเนินการตามifวิธีการและทดสอบได้ง่ายขึ้น ขอบคุณ!
Washington Botelho

25
ดูเหมือนว่าเราจะสร้าง Lisp ใน Handlebars
jdunning

8
คุณandและorผู้ช่วยทำงานกับพารามิเตอร์ 2 ตัวเท่านั้นซึ่งไม่ใช่สิ่งที่คุณต้องการ ฉันจัดการทำใหม่ทั้งandและตัวorช่วยเพื่อสนับสนุนพารามิเตอร์มากกว่าสองตัว การใช้งานนี้หนึ่งซับสำหรับand: return Array.prototype.slice.call(arguments, 0, arguments.length - 1).every(Boolean);และใช้หนึ่งซับสำหรับ:or return Array.prototype.slice.call(arguments, 0, arguments.length - 1).some(Boolean);
ลุค

3
รุ่นเล็ก ๆ น้อย ๆ ที่สูงขึ้นที่สามารถใช้หลายพารามิเตอร์ในแต่ละผู้ประกอบการ
เนท

87

เอาอันนี้ขึ้นรอยสำหรับพวกคุณที่อาศัยอยู่บนขอบ

สรุปสาระสำคัญ : https://gist.github.com/akhoury/9118682 การ สาธิต : ข้อมูลโค้ดด้านล่าง

ผู้ช่วยแฮนด์บาร์: {{#xif EXPRESSION}} {{else}} {{/xif}}

ผู้ช่วยในการดำเนินการคำสั่ง IF ด้วยการแสดงออกใด ๆ

  1. การแสดงออกเป็นสตริงหนีออกมาอย่างถูกต้อง
  2. ใช่คุณต้องหลบหนีอย่างถูกต้องตัวอักษรสตริงหรือเพียงแค่สลับคำพูดเดี่ยวและคู่
  3. คุณสามารถเข้าถึงฟังก์ชั่นระดับโลกหรือทรัพย์สินเช่น encodeURIComponent(property)
  4. ตัวอย่างนี้สมมติว่าคุณผ่านบริบทนี้ไปยังแฮนด์บาร์ของคุณการtemplate( {name: 'Sam', age: '20' } )แจ้งเตือนageคือstringเพื่อให้ฉันสามารถสาธิตparseInt()ได้ในภายหลังในโพสต์นี้

การใช้งาน:

<p>
 {{#xif " name == 'Sam' && age === '12' " }}
   BOOM
 {{else}}
   BAMM
 {{/xif}}
</p>

เอาท์พุต

<p>
  BOOM
</p>

JavaScript: (ขึ้นอยู่กับผู้ช่วยคนอื่น - อ่านต่อไป)

 Handlebars.registerHelper("xif", function (expression, options) {
    return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
  });

ผู้ช่วยแฮนด์บาร์: {{x EXPRESSION}}

ผู้ช่วยในการเรียกใช้นิพจน์จาวาสคริปต์

  1. การแสดงออกเป็นสตริงหนีออกมาอย่างถูกต้อง
  2. ใช่คุณต้องหลบหนีอย่างถูกต้องตัวอักษรสตริงหรือเพียงแค่สลับคำพูดเดี่ยวและคู่
  3. คุณสามารถเข้าถึงฟังก์ชั่นระดับโลกหรือทรัพย์สินเช่น parseInt(property)
  4. ตัวอย่างนี้จะถือว่าคุณผ่านบริบทนี้เพื่อแฮนด์ของคุณtemplate( {name: 'Sam', age: '20' } ), ageเป็นstringเพื่อวัตถุประสงค์ในการสาธิตก็สามารถเป็นอะไร ..

การใช้งาน:

<p>Url: {{x "'hi' + name + ', ' + window.location.href + ' <---- this is your href,' + ' your Age is:' + parseInt(this.age, 10)"}}</p>

เอาท์พุท:

<p>Url: hi Sam, http://example.com <---- this is your href, your Age is: 20</p>

JavaScript:

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

Handlebars.registerHelper("x", function(expression, options) {
  var result;

  // you can change the context, or merge it with options.data, options.hash
  var context = this;

  // yup, i use 'with' here to expose the context's properties as block variables
  // you don't need to do {{x 'this.age + 2'}}
  // but you can also do {{x 'age + 2'}}
  // HOWEVER including an UNINITIALIZED var in a expression will return undefined as the result.
  with(context) {
    result = (function() {
      try {
        return eval(expression);
      } catch (e) {
        console.warn('•Expression: {{x \'' + expression + '\'}}\n•JS-Error: ', e, '\n•Context: ', context);
      }
    }).call(context); // to make eval's lexical this=context
  }
  return result;
});

Handlebars.registerHelper("xif", function(expression, options) {
  return Handlebars.helpers["x"].apply(this, [expression, options]) ? options.fn(this) : options.inverse(this);
});

var data = [{
  firstName: 'Joan',
  age: '21',
  email: 'joan@aaa.bbb'
}, {
  firstName: 'Sam',
  age: '18',
  email: 'sam@aaa.bbb'
}, {
  firstName: 'Perter',
  lastName: 'Smith',
  age: '25',
  email: 'joseph@aaa.bbb'
}];

var source = $("#template").html();
var template = Handlebars.compile(source);
$("#main").html(template(data));
h1 {
  font-size: large;
}
.content {
  padding: 10px;
}
.person {
  padding: 5px;
  margin: 5px;
  border: 1px solid grey;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/1.11.1/jquery.min.js"></script>
<script src="http://cdnjs.cloudflare.com/ajax/libs/handlebars.js/1.0.0/handlebars.min.js"></script>

<script id="template" type="text/x-handlebars-template">
  <div class="content">
    {{#each this}}
    <div class="person">
      <h1>{{x  "'Hi ' + firstName"}}, {{x 'lastName'}}</h1>
      <div>{{x '"you were born in " + ((new Date()).getFullYear() - parseInt(this.age, 10)) '}}</div>
      {{#xif 'parseInt(age) >= 21'}} login here:
      <a href="http://foo.bar?email={{x 'encodeURIComponent(email)'}}">
        	http://foo.bar?email={{x 'encodeURIComponent(email)'}}
        </a>
      {{else}} Please go back when you grow up. {{/xif}}
    </div>
    {{/each}}
  </div>
</script>

<div id="main"></div>

Moar

หากคุณต้องการเข้าถึงขอบเขตระดับบนอันนี้จะแตกต่างกันเล็กน้อยการแสดงออกคือเข้าร่วมของข้อโต้แย้งทั้งหมดการใช้งาน: พูดว่าข้อมูลบริบทมีลักษณะเช่นนี้:

// data
{name: 'Sam', age: '20', address: { city: 'yomomaz' } }

// in template
// notice how the expression wrap all the string with quotes, and even the variables
// as they will become strings by the time they hit the helper
// play with it, you will immediately see the errored expressions and figure it out

{{#with address}}
    {{z '"hi " + "' ../this.name '" + " you live with " + "' city '"' }}
{{/with}}

javascript:

Handlebars.registerHelper("z", function () {
    var options = arguments[arguments.length - 1]
    delete arguments[arguments.length - 1];
    return Handlebars.helpers["x"].apply(this, [Array.prototype.slice.call(arguments, 0).join(''), options]);
});

Handlebars.registerHelper("zif", function () {
    var options = arguments[arguments.length - 1]
    delete arguments[arguments.length - 1];
    return Handlebars.helpers["x"].apply(this, [Array.prototype.slice.call(arguments, 0).join(''), options]) ? options.fn(this) : options.inverse(this);
});

15
มันเยี่ยมมาก
แฮนด์บาร์

1
:) ขอบคุณฉันปรับปรุงส่วนเล็กน้อยเพื่อเพิ่มสารพัดอีกสองสาม (ไม่เกี่ยวข้องโดยตรงกับคำถาม SO นี้ - แต่ด้วยจิตวิญญาณของการทำสิ่งที่คุณต้องการภายในแม่แบบแฮนด์บาร์) อย่างไรก็ตามคุณควรระวังข้อ จำกัด ฉันยัง ไม่พบวิธีในการเข้าถึง "ระดับขอบเขตบน" จากภายในนิพจน์บอกว่าคุณอยู่ในแต่ละขอบเขต{{#each}} ... {{xif ' ../name === this.name' }} {{/each}}... แต่ฉันยังคงตรวจสอบ ..
bentael

1
พบวิธีแก้ปัญหาสำหรับ "การเข้าถึงขอบเขตบน" แต่ใช้ตัวช่วยใหม่ดูคำตอบที่ปรับปรุงแล้ว{{z ...}}ผู้ช่วย
bentael

บูม! เยี่ยมมากตอนนี้ฉันสามารถทำสิ่งที่เป็นจริงได้ในขอบคุณบางส่วน !
AncAinu

1
@rickysullivan ลองแทนที่ของคุณeval(expression);ด้วย: eval('(function(){try{return ' + expression + ';}catch(e){}})();');- ฉันรู้ว่ามันน่าเกลียด แต่ก็ไม่สามารถคิดวิธีที่ปลอดภัยในการทำเช่นนี้ได้ - โปรดระวังว่านี่ไม่ใช่ "เร็ว" มากนักในการดำเนินการฉันจะไม่ทำสิ่งนี้ใน ซ้ำขนาดใหญ่
bentael

33

มีวิธีง่าย ๆ ในการทำเช่นนี้โดยไม่ต้องเขียนฟังก์ชันตัวช่วย ... สามารถทำได้ภายในเทมเพลตโดยสมบูรณ์

{{#if cond1}}   
  {{#if con2}}   
    <div> and condition completed</div>  
  {{/if}}
{{else}}   
  <div> both conditions weren't true</div>  
{{/if}}

แก้ไข: ในทางกลับกันคุณสามารถทำได้หรือทำได้โดยทำสิ่งนี้:

{{#if cond1}}  
  <div> or condition completed</div>    
{{else}}   
  {{#if cond2}}  
    <div> or condition completed</div>  
  {{else}}      
    <div> neither of the conditions were true</div>    
  {{/if}}  
{{/if}}

แก้ไข / หมายเหตุ: จากเว็บไซต์ของมือจับ: handlebarsjs.com นี่คือค่าที่ผิดพลาด:

คุณสามารถใช้ตัวช่วย if เพื่อสร้างบล็อกแบบมีเงื่อนไข หากอาร์กิวเมนต์ส่งคืน false, undefined, null, "" หรือ [] (ค่า "falsy") ดังนั้น 'cond' ใด ๆ (เช่น cond1 หรือ cond2) จะไม่ถูกนับว่าเป็นจริง


9
ไม่จริงๆคุณไม่ได้เปรียบเทียบค่าสองค่า แต่คุณต้องแน่ใจว่าทั้งสองมีอยู่มันต่างกัน
Cyril N.

3
ที่จริงแล้วมันประเมินว่ามันเป็นแบบเดียวกับที่จาวาสคริปต์ทำจากเว็บไซต์: "คุณสามารถใช้ตัวช่วย if เพื่อสร้างบล็อกแบบมีเงื่อนไขถ้าอาร์กิวเมนต์มันคืนค่าเป็นเท็จ, ไม่ได้กำหนด, ค่า null," "หรือ [] (ค่า" เท็จ) แฮนด์บาร์จะไม่แสดงผลบล็อก "
Jono

1
คุณคิดถูกแล้วก่อนอื่นฉันคิดว่าการทดสอบคือการเปรียบเทียบค่าสองค่าไม่ใช่การทดสอบทั้งสองค่า ไม่ดีขอโทษด้วย
Cyril N.

4
สิ่งนี้ทำงานไม่ถูกต้อง หาก cond1 เป็นจริงและ con2 เป็นเท็จจะไม่มีการพิมพ์อะไรเลย
Tessa Lau

1
เฮ้ @TessaLau ฉันคิดว่าฉันเห็นว่าคุณมาจากไหน อย่างไรก็ตามถ้าคุณวาดโฟลว์ควบคุมคุณจะเห็นว่าในบรรทัดแรกนั้นจะย้ายโฟลว์ควบคุมไปยังเงื่อนไขนั้น
Jono

19

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

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

Ember.Handlebars.registerHelper('ifeq', function(a, b, options) {
  return Ember.Handlebars.bind.call(options.contexts[0], a, options, true, function(result) {
    return result === b;
  });
});

คุณสามารถใช้สิ่งนี้:

{{#ifeq obj.some.property "something"}}
  They are equal!
{{/ifeq}}

นี่คือคำตอบที่เหมาะสมหากคุณใช้ Ember โซลูชันอื่น ๆ ที่คุณกล่าวถึงจะผ่านคีย์แทนค่า BTW ขอบคุณสำหรับสิ่งนี้ใช้เวลาสองสามชั่วโมงในการทำลายล้างหัว
J Lee

2
มีใครได้รับการทำงานกับ HTMLBars / Ember 1.10? Ember.Handlebars.bind ดูเหมือนจะไม่มีอีกต่อไป
ลินดา

ฉันเดิมใช้ registerBoundHelper แต่เดิม แต่เมื่อเปลี่ยนเงื่อนไขแล้วมันจะไม่เปลี่ยนไปเป็นค่าอื่นและย้อนกลับไป .. วิธีนี้ใช้ได้กับถ่านที่เปลี่ยนไป :) มันควรจะได้รับการโหวตมากขึ้น
Matt Vukomanovic

13

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

Handlebars.registerHelper("ifCond",function(v1,operator,v2,options) {
    switch (operator)
    {
        case "==":
            return (v1==v2)?options.fn(this):options.inverse(this);

        case "!=":
            return (v1!=v2)?options.fn(this):options.inverse(this);

        case "===":
            return (v1===v2)?options.fn(this):options.inverse(this);

        case "!==":
            return (v1!==v2)?options.fn(this):options.inverse(this);

        case "&&":
            return (v1&&v2)?options.fn(this):options.inverse(this);

        case "||":
            return (v1||v2)?options.fn(this):options.inverse(this);

        case "<":
            return (v1<v2)?options.fn(this):options.inverse(this);

        case "<=":
            return (v1<=v2)?options.fn(this):options.inverse(this);

        case ">":
            return (v1>v2)?options.fn(this):options.inverse(this);

        case ">=":
         return (v1>=v2)?options.fn(this):options.inverse(this);

        default:
            return eval(""+v1+operator+v2)?options.fn(this):options.inverse(this);
    }
});

คุณจะใช้มันในเทมเพลตอย่างไร? โดยเฉพาะส่วนที่ options.fn?
qodeninja

{{ifCond val1 '||' val2}} จริง {{else}} false {{/ if}} มันจะส่งคืน options.fn (จริงประโยค ifCond) ถ้าถูกต้องมิฉะนั้นจะส่งคืน options.inverse (false ประโยคอื่น ๆ ถ้าไม่ถูกต้อง)
Nick Kitto

1
เนื่องจากข้อแม้ที่กล่าวถึงการฉีดสคริปต์ฉันขอแนะนำอย่างยิ่งให้ใช้ตัวช่วยนี้ ใน codebase ขนาดใหญ่นี้สามารถรับผิดชอบปัญหาความปลอดภัยที่น่ารังเกียจได้อย่างง่ายดาย
Jordan Sitkin

8

นี่เป็นวิธีแก้ปัญหาหากคุณต้องการตรวจสอบเงื่อนไขหลายประการ:

/* Handler to check multiple conditions
   */
  Handlebars.registerHelper('checkIf', function (v1,o1,v2,mainOperator,v3,o2,v4,options) {
      var operators = {
           '==': function(a, b){ return a==b},
           '===': function(a, b){ return a===b},
           '!=': function(a, b){ return a!=b},
           '!==': function(a, b){ return a!==b},
           '<': function(a, b){ return a<b},
           '<=': function(a, b){ return a<=b},
           '>': function(a, b){ return a>b},
           '>=': function(a, b){ return a>=b},
           '&&': function(a, b){ return a&&b},
           '||': function(a, b){ return a||b},
        }
      var a1 = operators[o1](v1,v2);
      var a2 = operators[o2](v3,v4);
      var isTrue = operators[mainOperator](a1, a2);
      return isTrue ? options.fn(this) : options.inverse(this);
  });

การใช้งาน:

/* if(list.length>0 && public){}*/

{{#checkIf list.length '>' 0 '&&' public '==' true}} <p>condition satisfied</p>{{/checkIf}}

7

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

{{#compare Database.Tables.Count ">" 5}}
There are more than 5 tables
{{/compare}}

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

2
@augurone นี่ไม่ใช่ผู้ช่วยพื้นเมือง หากคุณไปที่ลิงก์คุณจะเห็นว่ามันเป็นตัวช่วยที่กำหนดเอง
gfullam

@ gfullam คุณถูกต้องฉันใช้มือจับในการประกอบซึ่งรวมถึงผู้ช่วยนี้โดยกำเนิด ความผิดฉันเอง.
augurone

4

คล้ายกับคำตอบของจิม แต่ใช้ความคิดสร้างสรรค์เล็กน้อยเราก็สามารถทำสิ่งนี้:

Handlebars.registerHelper( "compare", function( v1, op, v2, options ) {

  var c = {
    "eq": function( v1, v2 ) {
      return v1 == v2;
    },
    "neq": function( v1, v2 ) {
      return v1 != v2;
    },
    ...
  }

  if( Object.prototype.hasOwnProperty.call( c, op ) ) {
    return c[ op ].call( this, v1, v2 ) ? options.fn( this ) : options.inverse( this );
  }
  return options.inverse( this );
} );

จากนั้นจะใช้มันเราได้รับสิ่งที่ชอบ:

{{#compare numberone "eq" numbretwo}}
  do something
{{else}}
  do something else
{{/compare}}

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


3

อีกทางเลือกหนึ่งคือการใช้ชื่อฟังก์ชั่น#ifมา #ifจะตรวจสอบว่าพารามิเตอร์เป็นฟังก์ชั่นและถ้ามันเป็นแล้วมันจะเรียกมันและใช้ผลตอบแทนสำหรับการตรวจสอบ truthyness ด้านล่าง myFunction thisได้รับบริบทปัจจุบันเป็น

{{#if myFunction}}
  I'm Happy!
{{/if}}

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

ใช่คุณต้องเพิ่มฟังก์ชั่นในบริบท ในเว็บไซต์ที่ออกแบบมาไม่ดีใช่นี่อาจเป็นช่องโหว่ด้านความปลอดภัย แต่ในกรณีนั้นจะมีคนอื่นอีกหลายคน
Shital Shah

นี่คือวิธีที่ต้องการ .. TY
elad.chen

3

น่าเสียดายที่วิธีแก้ปัญหาเหล่านี้ไม่สามารถแก้ปัญหา "หรือ" ตัวดำเนินการ "cond1 || cond2" ได้

  1. ตรวจสอบว่าค่าแรกเป็นจริง
  2. ใช้ "^" (หรือ) และตรวจสอบว่า cond2 เป็นจริงหรือไม่

    {{#if cond1}} ทำการกระทำ {{^}} {{#if cond2}} ทำการกระทำ {{/ if}} {{/ if}}

มันแบ่งกฎ DRY ดังนั้นทำไมไม่ใช้บางส่วนเพื่อทำให้ยุ่งน้อยลง

{{#if cond1}}
    {{> subTemplate}}
{{^}}
    {{#if cond2}}
        {{> subTemplate}}
    {{/if}}
{{/if}}

3

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

ส่งต่อแฮนด์บาร์เมื่อแสดงผล:

var context= {
    'section1' : section1,
    'section2' : section2,
    'section1or2' : (section1)||(section2)
};

แล้วภายในแม่แบบแฮนด์บาร์ของคุณ:

{{#if section1or2}}
    .. content
{{/if}}

ฉันพูดถึงเรื่องนี้เพราะเห็นแก่ความเรียบง่ายและเพราะมันเป็นคำตอบที่อาจเร็วและเป็นประโยชน์ในขณะที่ยังคงปฏิบัติตามธรรมชาติของแฮนด์บาร์


3

ติดตั้งโปรแกรมเสริมEmber Truth Helpersโดยเรียกใช้คำสั่งด้านล่าง

ember ติดตั้ง ember-true-helpers

คุณสามารถเริ่มใช้ตัวดำเนินการทางตรรกะส่วนใหญ่ได้ (eq, ไม่ใช่ -q, ไม่ใช่, และ, หรือ, gt, gte, lt, lte, xor)

{{#if (or section1 section2)}}  
...content  
{{/if}}

คุณสามารถรวม subexpression เพื่อดำเนินการต่อไป

{{#if (or (eq section1 "section1") (eq section2 "section2") ) }}  
...content  
{{/if}}

2

อีกวิธีแก้ปัญหาที่คดเคี้ยวสำหรับผู้ช่วยประกอบไปด้วย:

'?:' ( condition, first, second ) {
  return condition ? first : second;
}

<span>{{?: fooExists 'found it' 'nope, sorry'}}</span>

หรือผู้ช่วยที่รวมตัวกันง่าย ๆ :

'??' ( first, second ) {
  return first ? first : second;
}

<span>{{?? foo bar}}</span>

เนื่องจากตัวละครเหล่านี้ไม่มีความหมายพิเศษในมาร์กอัปแฮนด์บาร์คุณจึงสามารถใช้มันเพื่อชื่อผู้ช่วยได้


1

ฉันได้พบแพ็คเกจ npm ที่สร้างด้วย CoffeeScript ซึ่งมีผู้ช่วยที่มีประโยชน์มากมายสำหรับแฮนด์บาร์ ดูเอกสารประกอบใน URL ต่อไปนี้:

https://npmjs.org/package/handlebars-helpers

คุณสามารถทำการwget http://registry.npmjs.org/handlebars-helpers/-/handlebars-helpers-0.2.6.tgzดาวน์โหลดและดูเนื้อหาของแพ็คเกจ

คุณจะสามารถทำสิ่งต่าง ๆ เช่น{{#is number 5}}หรือ{{formatDate date "%m/%d/%Y"}}


1

หากคุณต้องการตรวจสอบว่ามีองค์ประกอบหนึ่งหรือไม่อยู่คุณสามารถใช้ผู้ช่วยที่กำหนดเองนี้

Handlebars.registerHelper('if_or', function(elem1, elem2, options) {
  if (Handlebars.Utils.isEmpty(elem1) && Handlebars.Utils.isEmpty(elem2)) {
    return options.inverse(this);
  } else {
    return options.fn(this);
  }
});

แบบนี้

{{#if_or elem1 elem2}}
  {{elem1}} or {{elem2}} are present
{{else}}
  not present
{{/if_or}}

หากคุณต้องมี "หรือ" เพื่อเปรียบเทียบ ค่าส่งคืนฟังก์ชัน ฉันต้องการเพิ่มคุณสมบัติอื่นที่ส่งกลับผลลัพธ์ที่ต้องการ

เทมเพลตควรเป็นแบบไร้เหตุผลหลังจากทั้งหมด!



1

เพิ่งมาถึงโพสต์นี้จากการค้นหาของ Google เกี่ยวกับวิธีการตรวจสอบว่าสตริงเท่ากับสตริงอื่น

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

สิ่งที่คนลืมคือวัตถุบางอย่างมีการสืบทอดฟังก์ชันที่สามารถใช้ในเทมเพลตหนวด ในกรณีของสตริง:

https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/match

An Array containing the entire match result and any parentheses-captured matched results; null if there were no matches.

เราสามารถใช้วิธีนี้เพื่อส่งคืนชุดการแข่งขันหรือnullหากไม่พบรายการที่ตรงกัน สิ่งนี้สมบูรณ์แบบเพราะดูที่เอกสารประกอบ HandlebarsJS http://handlebarsjs.com/builtin_helpers.html

You can use the if helper to conditionally render a block. If its argument returns false, undefined, null, "", 0, or [], Handlebars will not render the block.

ดังนั้น...

{{#if your_string.match "what_youre_looking_for"}} 
String found :)
{{else}}
No match found :(
{{/if}}

UPDATE:

หลังจากการทดสอบบนเบราว์เซอร์ทั้งหมดนี้ไม่ได้ทำงานบน Firefox HandlebarsJS ส่งผ่านอาร์กิวเมนต์อื่น ๆ ไปยังการเรียกใช้ฟังก์ชันซึ่งหมายความว่าเมื่อมีการเรียกใช้ String.prototype.match อาร์กิวเมนต์ที่สอง (เช่นค่าสถานะ Regexp สำหรับการเรียกใช้ฟังก์ชันการจับคู่ตามเอกสารด้านบน) จะปรากฏขึ้น Firefox มองว่าสิ่งนี้เป็นการใช้งาน String.prototype.match ที่เลิกใช้แล้วและอื่น ๆ

วิธีแก้ปัญหาคือการประกาศต้นแบบการทำงานใหม่สำหรับวัตถุ String JSและใช้สิ่งนั้นแทน:

if(typeof String.includes !== 'function') {
    String.prototype.includes = function(str) {
        if(!(str instanceof RegExp))
            str = new RegExp((str+'').escapeRegExp(),'g');
        return str.test(this);
    }
}

ตรวจสอบให้แน่ใจว่ามีรหัส JS นี้ก่อนที่คุณจะเรียกใช้ฟังก์ชัน Handlebars.com () ของคุณจากนั้นในแม่แบบของคุณ ...

{{#your_string}}
    {{#if (includes "what_youre_looking_for")}} 
        String found :)
    {{else}}
        No match found :(
    {{/if}}
{{/your_string}}

อัปเดตสำหรับ Firefox นี่คือสิ่งที่ฉันใช้อยู่ในปัจจุบัน
Jon

1

ที่นี่เรามีแฮนด์วานิลลาสำหรับหลายตรรกะ && และ || (และหรือ):

Handlebars.registerHelper("and",function() {
    var args = Array.prototype.slice.call(arguments);
    var options = args[args.length-1];

    for(var i=0; i<args.length-1; i++){
        if( !args[i] ){
            return options.inverse(this);
        }
    }

    return options.fn(this);
});


Handlebars.registerHelper("or",function() {
    var args = Array.prototype.slice.call(arguments);
    var options = args[args.length-1];

    for(var i=0; i<args.length-1; i++){
        if( args[i] ){
            return options.fn(this);
        }
    }

    return options.inverse(this);
}

// Results
// {{#and foo bar sally bob}} yup {{else}} nope {{/and}} // yup
// {{#or foo bar "" sally bob}} yup {{else}} nope {{/or}} // yup

// {{#and foo bar "" sally bob}} yup {{else}} nope {{/and}} // nope
// {{#or "" "" "" "" ""}} yup {{else}} nope {{/or}} // nope

ไม่แน่ใจว่าหาก "ปลอดภัย" ใช้ "และ" และ "หรือ" ... "อาจเปลี่ยนเป็นบางสิ่งเช่น" op_and "และ" op_or "หรือไม่


1

ทางออกที่ถูกต้องสำหรับ AND / หรือ

Handlebars.registerHelper('and', function () {
    // Get function args and remove last one (function name)
    return Array.prototype.slice.call(arguments, 0, arguments.length - 1).every(Boolean);
});
Handlebars.registerHelper('or', function () {
    // Get function args and remove last one (function name)
    return Array.prototype.slice.call(arguments, 0, arguments.length - 1).some(Boolean);
}); 

จากนั้นโทรมาดังนี้

{{#if (or (eq questionType 'STARTTIME') (eq questionType 'ENDTIME') (..) ) }}

BTW: โปรดทราบว่าการแก้ปัญหาที่ให้ไว้ที่นี่ไม่ถูกต้องเขาไม่ได้ลบอาร์กิวเมนต์สุดท้ายซึ่งเป็นชื่อฟังก์ชั่น https://stackoverflow.com/a/31632215/1005607

AND / OR ดั้งเดิมของเขาขึ้นอยู่กับรายการอาร์กิวเมนต์ทั้งหมด

   and: function () {
        return Array.prototype.slice.call(arguments).every(Boolean);
    },
    or: function () {
        return Array.prototype.slice.call(arguments).some(Boolean);
    }

บางคนเปลี่ยนคำตอบได้ไหม ฉันเสียเวลาหนึ่งชั่วโมงในการพยายามแก้ไขบางสิ่งด้วยคำตอบที่แนะนำโดย 86 คน การแก้ไขคือการกรองอาร์กิวเมนต์สุดท้ายซึ่งเป็นชื่อฟังก์ชั่นArray.prototype.slice.call(arguments, 0, arguments.length - 1)


0

การปฏิบัติตาม 2 แนวทางเหล่านี้ช่วยให้ผู้ใช้กำหนดคำสั่งกำหนดเองผูกมัด if-statementและผู้ช่วยที่กำหนดเองที่กำหนดเองฉันสามารถปรับมุมมองที่ใช้ร่วมกันของฉันในโพสต์นี้บนstackoverflowเพื่อใช้แทนมาตรฐาน # ถ้าคำสั่ง สิ่งนี้ควรมีความปลอดภัยมากกว่าเพียงแค่โยน # ถ้ามี

ผู้ช่วยที่ถูกผูกไว้ที่กำหนดเองในส่วนสำคัญนั้นโดดเด่น

<li>
    <a href="{{unbound view.varProductSocialBlog}}">
        {{#if-equal view.showDiv "true"}}<div>{{/if-equal}}<i class="fa fa-rss-square"></i>{{#if-equal view.showDiv "true"}}</div>{{/if-equal}}
        {{#if-equal view.showTitle "true"}}Blog{{/if-equal}}
    </a>
</li>

ฉันใช้โปรเจ็กต์ember cliเพื่อสร้างแอ็พพลิเคชัน ember ของฉัน

การตั้งค่าปัจจุบัน ณ เวลาที่โพสต์นี้:

DEBUG: -------------------------------
DEBUG: Ember      : 1.5.1
DEBUG: Ember Data : 1.0.0-beta.7+canary.b45e23ba
DEBUG: Handlebars : 1.3.0
DEBUG: jQuery     : 2.1.1
DEBUG: -------------------------------


0

คุณสามารถทำได้โดยใช้ตัวดำเนินการแบบลอจิคัลดังนี้:

{{#if (or(eq firstValue 'String_to_compare_value') (eq secondValue 'String_to_compare_value'))}}business logic goes here{{/if}}

{{#if (and(eq firstValue 'String_to_compare_value') (eq secondValue 'String_to_compare_value'))}}business logic goes here{{/if}}

ก่อนปิดถ้าคุณสามารถเขียนตรรกะทางธุรกิจของคุณ


0

คุณสามารถใช้รหัสต่อไปนี้:

{{#if selection1}}
    doSomething1
{{else}}
   {{#if selection2}}
       doSomething2
   {{/if}}
{{/if}}

โปรดอธิบายเพิ่มเติมเกี่ยวกับความคิดและกระบวนการของคุณมันอาจเป็นเรื่องยากสำหรับคนที่จะเข้าใจวิธีการแก้ปัญหาของคุณหากพวกเขาไม่รู้จักบริบทหรือเป็นเรื่องใหม่สำหรับภาษา
mrhn

-1

นี่เป็นวิธีที่ฉันใช้สำหรับ ember 1.10 และ ember-cli 2.0

// app/helpers/js-x.js
export default Ember.HTMLBars.makeBoundHelper(function (params) {
  var paramNames = params.slice(1).map(function(val, idx) { return "p" + idx; });
  var func = Function.apply(this, paramNames.concat("return " + params[0] + ";"))
  return func.apply(params[1] === undefined ? this : params[1], params.slice(1));
});

จากนั้นคุณสามารถใช้มันในเทมเพลตของคุณเช่นนี้:

// used as sub-expression
{{#each item in model}}
  {{#if (js-x "this.section1 || this.section2" item)}}
  {{/if}}
{{/each}}

// used normally
{{js-x "p0 || p1" model.name model.offer.name}}

ไหนขัดแย้งในการแสดงออกจะถูกส่งผ่านในขณะที่p0, p1, p2ฯลฯ และยังสามารถอ้างอิงเป็นp0this

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