6

I am trying to rewrite a JS/jQuery SPA using AngularJS. The SPA is an item editing grid using the amazing Handsontable. I was delighted to see that there is an AngularJS version in the works, and that, for the most part, it works just as expected.

However, I'm having a hard time figuring out how to do something I am doing in jQuery in AngularJS. The grid has (among others) 3 columns, one of which is dependent on the other two. The three columns are cost, selling price and margin ( which is (1 - (cost / price)) * 100 ). I can do the math in the controller and pass the margins into the $scope.items variable for the initial loading, but it obviously does not update when you change the cost or price columns. It seems like the perfect place to do two-way data binding, but I can't figure out how to bind the datacolumn value of margin to the value of two other rows. I feel like there is an obvious way to do this, but since I'm new to AngularJS, I thought I might throw it out to the community.

Thanks in advance!

Edit:

So after playing around with some of the answers, I want to refine my question a bit to get to the heart of the matter. I have a REST API serving a JSON document that contains arrays of items from an online point of sale service. Here is a sample with dummy data:

{ '@attributes': {'count':10}, 'Item': [
  { 'customSku':'sampleItem1'
  , 'description':'Sample Item, first'
  , 'defaultCost':'10.00'
  , 'Prices': {'ItemPrice':[
      {'amount':'17.99', 'useType':'Default'}
    , {'amount':'19.99', 'useType':'MSRP'}
    ]}
  }
, { 'customSku':'sampleItem2'
  , 'description':'Item, Sample, 2nd'
  , 'defaultCost':'20.00'
  , 'Prices': {'ItemPrice':[
      {'amount':'29.99', 'useType':'Default'}
    , {'amount':'39.99', 'useType':'MSRP'}
    ]}
  }
]}

I'm not joking that the data is structured like this, it totally is. Anyway.

Right now, I am passing this to the view in my controller like this: $scope.items = JSON.parse(request.responseText);. How do I pass the function: $scope.getMargin = function() { return (1 - (cost / price)) * 100 } to the view in such a way that cost and price are scoped to the correct item? I feel like I am really close to getting it, but I haven't quite gotten there. Thanks to the answerers so far!

Edit 2:

Plunker here, as requested!

scott_trinh
  • 155
  • 1
  • 8

3 Answers3

0

You will need to add a watch for one of your $scope vars: see

How do I use $scope.$watch and $scope.$apply in AngularJS?

If you render the row with the angular ngRepeat, each item will have the correct indexed-scope as watchers are created for each scoped item in the row + 1 for the entire ngRepeat

http://docs.angularjs.org/api/ng.directive:ngRepeat

Community
  • 1
  • 1
Nikos
  • 7,295
  • 7
  • 52
  • 88
  • I think the answer is in there, somewhere, but it isn't quite this simple. From what I can tell, changing data in the handsontable does not cause the $scope to change. I might have to dig around in the source to verify. – scott_trinh Feb 13 '14 at 21:07
  • I stand corrected. I just did a quick and dirty test, and the td's values are bound to the $scope models. Now, if I can just figure out how to make the margin column dynamic... – scott_trinh Feb 13 '14 at 21:15
  • @scott_trinh can you knock up a plunker? – Nikos Feb 14 '14 at 10:07
  • I'll put one together. I'm not building it using ng-repeat, unfortunately, as that would be real easy. :) I'm using the AngularJS version of Handsontable linked in the question which has something similar going on under the hood, but doesn't use {{}} syntax. I'll work on a Plunker and get it up ASAP. Thanks for your time!! – scott_trinh Feb 14 '14 at 12:56
0

You can bind the third column data to an expression that is a function that evaluates the value

$scope.getValue=function() {
   return 1 - (cost / price)) * 100;
}

and in the binding just do

<div>{{getValue()}}</div>
Chandermani
  • 42,589
  • 12
  • 85
  • 88
  • I'm not sure that works for my specific application since the handsontable can't use {{}} syntax to fill the table values, from what I can tell. – scott_trinh Feb 13 '14 at 21:02
  • I am also kinda wrong about this! You can't use the {{}} syntax, but referencing a function in the datacolumn value works. I still can't quite figure out how to define the function, or where. Let me edit my question to include some more detail. – scott_trinh Feb 13 '14 at 21:23
0

I'm really new to Handsontable, but my idea is that you could use a custom renderer, and then get calculate the margin inside the renderer.

Not sure if my approach works with the handsontable you are using or used in 2014, but for Handsontable 0.26.x and ngHandsontable 0.12.0 it should work. I just had a very similar question, where I just found a solution (see handsontable: how to use an entire datarow (object) as data for a hot-column?), therefore I think it could also work for you as well.

Use a custom renderer (and editor, but I guess the margin column will be read-only anyway)

<datacolumn read-only renderer="marginRenderer" width="80" title="'Margin'"></datacolumn>

which then gets the data from the table data source and does the calculation inside the renderer.

  $scope.marginRenderer = function (instance, td, row, col, prop, value, cellProperties) {
      var translatedRow = instance.getPlugin('columnSorting').translateRow(row);
      var rowData = instance.getSourceData()[translatedRow];
      td.innerHTML = '' + ((1 - (rowData.cost / rowData.price)) * 100);
      return td;
  }
Community
  • 1
  • 1
Mathias Conradt
  • 28,420
  • 21
  • 138
  • 192