1

At the moment I have this in my template:

  • WAY 1

          <template is="dom-if" if="{{_showCancel(userData.generic.id,info.userId,info._children.gigId.userId,info.accepted,info.cancelled,info.paymentTerms)}}">
    

And then this in my element:

  _showCancel: function(viewerUserId,offerUserId,gigUserId, accepted, cancel, paymentTerms) {

    // Offer needs to be accepted and NOT cancelled
    if (!info.accepted || info.cancelled) return false;

    // Gig owner can ALWAYS cancel an offer
    if (viewerUserId == gigUserId ) return true;

    // Offer maker can only cancel if the gig involved pre-payment
    if (viewerUserId == offerUserId && paymentTerms != 'on-day') return true;

    return false;
  },

Do I have just too many parameters to this function?

  • WAY 2

Should I just have something like this instead:

<template is="dom-if" if="{{_showCancel(userData, info)}}">
  • WAY 3

Although I would want to check if their sub-properties change too... so I would need:

<template is="dom-if" if="{{_showCancel(userData, info, userData.*, info.*)}}">
  • WAY 4

But then again I probably should just look for the properties and use the value property like so:

<template is="dom-if" if="{{_showCancel(userData.*, info.*)}}">

And then the function would be:

_showCancel: function(userDataObs, infoObs) {
  var userData = userDataObs.value;
  var info = infoObs.value;

  if( !userData || !info) return;

  ...

Questions:

  • Do you see any fundamental mistakes with ways 1 to 4?
  • Is WAY 1 really the best way to go about it? (it feels like it right now)
  • Is WAY 3 an acceptable pattern?

ADDENDUM

What would I do in cases where I have:

<paper-button 
  raised 
  hidden$="{{!_showCancel(item.id,userData.config.usersCategories,userData.config.userCategories,userData.config.usersCategories.*)}}"
>
Cancel
</paper-button>

(NOTE: usersCategories is an Array) And _showCancel being:

  _showCancel: function(categoryId, userCategories) {
    for (var k in usersCategories) {
      var $ucat = usersCategories[k];

      if ($ucat.categoryId == categoryId) {
        if ($ucat.status == 'applied' || $ucat.status == 'granted') return true;
      }
    }

    return false;
  },

The point being: I want both easy access to usersCategories, but don't want to get the value out of the "strange" array modifiers etc. So, is this a good pattern?

Merc
  • 16,277
  • 18
  • 79
  • 122
  • For the addendum: if you wish to detect array mutations, you need to listen to `.*` or `.splices` ([see spec](https://www.polymer-project.org/1.0/docs/devguide/observers#array-observation)). If you don't like _splices_ (the _strange modifiers_), then you can ignore them inside your function. But you should still pass both the array and the `.*` or `.splices` variable. Without it, the change won't be properly propagated. Your current code is actually OK. Except maybe for the `for/in`iteration. This is not a good way to iterate an array, use a simple `for` with `i` for index. – alesc Feb 15 '17 at 16:57
  • I have recently discovered that I could iterate an array with a for statement. I found it much easier! What's wrong with that? – Merc Feb 16 '17 at 00:01
  • All good I just figured it out :D http://stackoverflow.com/questions/500504/why-is-using-for-in-with-array-iteration-a-bad-idea – Merc Feb 16 '17 at 00:09

1 Answers1

1

The "Way 1" is the right one. But, you should only reference the variables that you need, and you should always use them as they are defined in your function header.

For example, you use:

{{_showCancel(userData.generic.id,info.userId,info._children.gigId.userId,info.accepted,info.cancelled,info.paymentTerms)}}

with the following function header:

_showCancel: function(viewerUserId,offerUserId,gigUserId, accepted, cancel, paymentTerms).

But then inside the function, you reference info.accepted and info.cancelled, whereas you should used accepted and cancelled.

This is because inside the function, the referenced values will always be up-to-date, whereas referencing variables via this.<variable-name> can sometimes contain older values.

In order for my answer to be complete, I will also explain certain "problems" with other ways.

Way 2: Here, you only reference the Object as a whole. This won't trigger the call via subproperty change, so it won't work as desired.

Way 3 and Way 4 are similar, and both are overkills. With the object.* notation, you listen to all subproperty changes, which you most likely don't want.

tl;dr

Go with "Way 1" and make things simpler by using computed properties.

To do this, change:

<template is="dom-if" if="{{_showCancel(userData.generic.id,info.userId,info._children.gigId.userId,info.accepted,info.cancelled,info.paymentTerms)}}">

To:

<template is="dom-if" if="{{isCanceled}}">

And add the following computed property to the Polymer element:

isCanceled: {
  type: Boolean,
  computed: '_showCancel(userData.generic.id,info.userId,info._children.gigId.userId,info.accepted,info.cancelled,info.paymentTerms)'
}

You already have _showCancel defined, so this is actually it. The code should work the same as your "Way 1" example, only the dom-if is cleaner. This is especially useful if you re-use the condition on multiple occurences.

If you have any questions, don't hesitate do add a comment about it.

alesc
  • 2,776
  • 3
  • 27
  • 45
  • Having the full info.cancelled is a typo leftover from cut and pasting code. Thank you for your answer, especially for the computed property idea!!! – Merc Feb 15 '17 at 14:17
  • Thank you for the awesome answer. I accepted it... and I also added an addendum question. I know there is no incentive to you to answer, but it would be awesome if you could :D – Merc Feb 15 '17 at 15:02