ui-grid – Brian Hann https://brianhann.com Thu, 14 Dec 2017 17:57:52 +0000 en-US hourly 1 87222886 UI-Grid and Multi-Select https://brianhann.com/ui-grid-and-multi-select/ https://brianhann.com/ui-grid-and-multi-select/#comments Tue, 01 Dec 2015 17:23:28 +0000 https://brianhann.com/?p=732 In a previous post we explored how to use dropdown widgets with UI-Grid. That’s great if your model allows just one value, but what if you want to allow multiple values? Here’s an example: you’ve got a list of people who can each speak one or more languages. Your users need to be able select multiple […]

The post UI-Grid and Multi-Select appeared first on Brian Hann.

]]>
In a previous post we explored how to use dropdown widgets with UI-Grid. That’s great if your model allows just one value, but what if you want to allow multiple values?

Here’s an example: you’ve got a list of people who can each speak one or more languages. Your users need to be able select multiple options from a list of languages for each person in your data set. How do you do it?

Thankfully, UI-Select already has multi-select built in. All we need to do is take the code from our previous post and tweak it a bit.

Tweak the Theme

First off, it doesn’t look like we can use the selectize theme for multi-select. We’ll have to use select2. This is as simple as swapping out the theme property in our ui-select directive: theme="select2".

That only changes the theme; it doesn’t enable multi-selection. In order to do that we need to also add the multiple property.

We’ll also need to swap out our reference to the selectize CSS for select2’s: https://cdnjs.cloudflare.com/ajax/libs/select2/3.4.5/select2.min.css

This will have our select box looking like this: Multi-select example

Comma Separated Values

Next, in our grid we want to see the values separated by commas. We can create a custom cell template that joins the column’s field with the join operator:

<div class="ui-grid-cell-contents">
  {{ COL_FIELD.join(', ') }}
</div>

There’s other ways you could choose to display the selected values, of course. Comma-separated is easy to implement though.

Change the Placeholder

Finally, we need to make sure the selected options show up properly. Inside the ui-select-match directive we change {{ blah }} to {{ $item }}. Our final UI-Select markup looks like this:

<ui-select-wrap>
  <ui-select
    multiple
    ng-model="MODEL_COL_FIELD"
    theme="select2"
    ng-disabled="disabled"
    append-to-body="true">
    
    <ui-select-match placeholder="Choose...">{{ $item }}</ui-select-match>
    <ui-select-choices repeat="item in col.colDef.editDropdownOptionsArray | filter: $select.search">
      <span>{{ item }}</span>
    </ui-select-choices>
  </ui-select>
</ui-select-wrap>

 

Example

Here’s our updated plunker:

Have questions or suggestions? Leave them in the comments below!




Sign Up

Like what you read? Sign up for more! I love sharing tips, tricks, and methods to make web development faster and easier. And believe me, I hate spam just as much as you do.

The post UI-Grid and Multi-Select appeared first on Brian Hann.

]]>
https://brianhann.com/ui-grid-and-multi-select/feed/ 7 732
UI Grid 3.0 Released! https://brianhann.com/ui-grid-3-0-released/ https://brianhann.com/ui-grid-3-0-released/#comments Mon, 20 Jul 2015 17:08:17 +0000 https://brianhann.com/?p=686 Last week UI Grid finally had its first stable release. This has been a long, long (too long) road. It started about 2 years ago as a rewrite of ng-grid, which was itself an AngularJS port of SlickGrid. And now UI Grid is stable! Well, almost… The core of UI Grid is all stable, but some of the […]

The post UI Grid 3.0 Released! appeared first on Brian Hann.

]]>
Last week UI Grid finally had its first stable release.

This has been a long, long (too long) road. It started about 2 years ago as a rewrite of ng-grid, which was itself an AngularJS port of SlickGrid. And now UI Grid is stable!

Well, almost… The core of UI Grid is all stable, but some of the features are a little behind. More on that later.

Want to give UI Grid a try? Head on over to to GitHub repo for installation instructions, and visit the Tutorials to learn how to use it.

Thank You

The team and I really want to thank the community for being so great, helpful, and patient with us. I don’t think any of us thought that this project would see so much use, and it’s been a real battle to make time to give you all the support you deserve!

Despite the slow timetable, you all have been super supportive, and that means a ton. Thank you.

Lessons Learned

This is the largest Open Source project I’ve worked on, and I’ve learned a ton. Here’s a few of the most important ones:

1. Start small, even with a rewrite

Our assumption was that because ng-grid had a bunch of features included (grouping, filtering, etc), we needed to include them all. Bzzzt, wrong!

We would have served the community better by getting a small release out the door with little functionality rather than trying to cram in everything v2.x had. This greatly lengthened our time-to-release.

We also ended up including more features that ng-grid did not have.  We should have scheduled those for a later release and really focused on getting the core stable and complete.

2. Schedule Responding to User Issues

Responding to issues on GitHub has largely been a catch-as-catch-can operation. I’ve done it when I can make time, and try to hit as many issues as I can.

Instead what I should have done is make a regular time-based schedule for organizing and responding to issues. This would make sure more people get their problems heard, understood, and solved.

3. Set up Style Guide in Advance

We all have our own style when it comes to writing code, and that’s cool, but for a large project having a consistent style is really important.  Debugging issues and reading 3 or 4 different ways of writing blocks, variable definitions, etc. gets really tiring.

We should have set up JSCS in advance, instead of late into the rewrite.

4. Trick People into Helping

OK not really.

But getting help is a must for a larger project. Our team has shrunk and grown multiple times over the past few years. Having multiple people means we can cover each other and help our users faster.

We’re incredibly lucky to have people who contribute on such a regular basis, those in the community included.

What’s Next

As I said in the beginning, the core of UI Grid is stable but some features are aren’t. They’re still alpha or beta. You can find a full list of the features on GitHub.

We plan to keep tackling bugs, increasing stability, and addressing new releases of Angular, including Angular 2.

Live Q&A

With 3.0 released, we want to give the chance for the community to ask any and all questions of the team. We also want to know what you’re doing with UI Grid, and what you interested in for the future.

We’ll be doing a live Q&A session on Saturday, August 8th @ 12:00PM CDT. If you want reminders and a link to the recording once it’s over, head on over to the event page.

UI Grid

Or if you just want to join the event live you can go to the Hangout page.

We look forward to seeing you all there!

The post UI Grid 3.0 Released! appeared first on Brian Hann.

]]>
https://brianhann.com/ui-grid-3-0-released/feed/ 2 686
Easily Import Spreadsheets into UI-Grid https://brianhann.com/easily-import-spreadsheets-into-ui-grid/ https://brianhann.com/easily-import-spreadsheets-into-ui-grid/#comments Wed, 24 Jun 2015 22:23:51 +0000 https://brianhann.com/?p=565 Your desktop spreadsheet tools need to handle all sorts of different proprietary file formats. Why should your web tools be different? Unfortunately in the past it has been pretty much impossible to get JavaScript in the browser to be able to read XLS, ODS, and all the others. Thankfully, these days there’s libraries that will do the heavy lifting for […]

The post Easily Import Spreadsheets into UI-Grid appeared first on Brian Hann.

]]>
Your desktop spreadsheet tools need to handle all sorts of different proprietary file formats. Why should your web tools be different?

Unfortunately in the past it has been pretty much impossible to get JavaScript in the browser to be able to read XLS, ODS, and all the others. Thankfully, these days there’s libraries that will do the heavy lifting for us. In this post we’ll look at importing data from spreadsheets right into your UI-Grid with SheetJS.

Bonus: Want to export grid data as a spreadsheet file, right in the browser, no servers involved? [leadbox_link interest=”ui-grid” title=”Get the Guide” text=”Enter your email to get the bonus content link.” button=”Send it to Me!”]Enter your email and get a link to the bonus area[/leadbox_link]. It has a how-to guide that will walk you through it.

SheetJS: In-Browser Spreadsheets

SheetJS is a github organization that provides several node.js and browser-based libraries for reading and writing spreadsheets, in a lot of different formats.  Just to name a few, it can handle:

  • XLS
  • XLSX
  • CSV / TSV
  • ODS

For me this covers all my use cases. Our office is split into people using current MS products, people with Open Office, and tools that export old XLS files.

The specific libraryies we’ll be using is js-xlsx, which will handle Excel files, and the add-on ods.js that will handle Open Office files.

Example App

We’re going to show off a little example that will fill out a UI-Grid instance from a spreadsheet file the user selects.

First we need to make a way to select the file. We’ll do this by transcluding content into the grid.

Want to read more about UI-Grid and transclusion. Check out this post: UI-Grid: The Easiest Customization You’ll Ever Write

<div ng-controller="MainCtrl as vm">

<div id="grid1" ui-grid="vm.gridOptions" class="grid">
  <div class="grid-msg-overlay" ng-show="!vm.gridOptions.data.length">
    <div class="msg">
      <div class="center">
        <span class="muted">Select Spreadsheet File</span>
        <br />
        <input type="file" accept=".xls,.xlsx,.ods" fileread opts="vm.gridOptions" multiple="false" />
      </div>
    </div>
  </div>
</div>

</div>

UI-Grid will bring all the content inside <div ui-grid></div> into itself, at its own scope. We can use this to display a “Select File” message when there’s no rows.  Inside this message we have an <input type="file"> that accepts spreadsheet files. It also has a fileread attribute. That’s our directive that will do the heavy lifting.

The directive needs to do three things:

  • Listen for changes to the file input and handle selected files
  • Read the file contents in and process them with js-xls
  • Turn the output from js-xls into column definitions and data rows for UI-Grid

We can use the change event handler to be notified when the user selects a file. When that happens we will create a new FileReader  that will read the file in as a binary string, and we’ll process the data in the onload handler. The directive initially looks like this:

.directive("fileread", [function () {
  return {
    link: function ($scope, $elm, $attrs) {
      $elm.on('change', function (changeEvent) {
        var reader = new FileReader();
        
        reader.onload = function (evt) {
          var data = evt.target.result;
        };
        
        reader.readAsBinaryString(changeEvent.target.files[0]);
      });
    }
  }
}]);

Now we have data in our onload handler which has the contents of the spreadsheet file. We next need to process data with js-xlsx.

.directive("fileread", [function () {
  return {
    scope: {
      opts: '='
    },
    link: function ($scope, $elm, $attrs) {
      $elm.on('change', function (changeEvent) {
        var reader = new FileReader();
        
        reader.onload =function (evt) {
          $scope.$apply(function () {
            var data = evt.target.result;
            
            var workbook = XLSX.read(data, {type: 'binary'});
            
            var headerNames = XLSX.utils.sheet_to_json(
                                workbook.Sheets[workbook.SheetNames[0]],
                                { header: 1 }
                              )[0];
            
            var data = XLSX.utils.sheet_to_json(workbook.Sheets[workbook.SheetNames[0]]);
            
            $scope.opts.columnDefs = [];
            headerNames.forEach(function (h) {
              $scope.opts.columnDefs.push({ field: h });
            });
            
            $scope.opts.data = data;
            
            $elm.val(null);
          });
        };
        
        reader.readAsBinaryString(changeEvent.target.files[0]);
      });
    }
  }
}]);

Here we’ve added a scope property to the directive to allow us to pass in the grid’s options object. We’ll use this to get our column defs and rows into the grid.

XLSX.read(data) returns a workbook object to us. This contains two properties, one is an array called SheetNames that has the list of sheets in the workbook. The other property is Sheets which is a dictionary of sheet names whose value is their Sheet object.

The Sheet object, then,  is just a dictionary of of cell positions whose value is the cell information (value, formatting, etc). It kinda looks like:

{
  A1: {
    t: 's', // Cell type. "s" for "String"
    v: 'my raw value'
    w: 'my formatted value'
  },
  A2: {
    t: 's',
    v: 'Another value',
    w: 'Another value'
  }
}

This would be a pain to manually process. Luckily there’s a utility method in XLSX.utils called sheet_to_json. We can pass it the sheet and get the data back as a JSON object. It will even use the first row as the column names for the keys. Yay!

That’s our row data. We could stop here if we didn’t care about the column defs, but if we do we can use XLSX.utils.sheet_to_json again with the argument { header: 1 }. This will return a simple array of arrays where each element of the outer array is a row and each element of the inner arrays is the set of columns for that row. We can use the first row as our column names and iterate over each of them, creating a column def for each one.

At last, here’s our working demo!

Hopefully you have a spreadsheet sitting around. If not you can use this one. Save that link to your desktop/device and insert it into the demo above.

Summary

That’s about the long and short of it! You may have problems with specific file types; check out the SheetJS demo pages to see if your files work there, and do some debugging in your developer console to see what’s going on; js-xlsx is pretty good about giving useful exceptions.

Other things you could do with this:

  • Add a drag-and-drop target inside the grid.
  • Allow uploading multiple files and combine them all into one grid
  • Download a spreadsheet from UI-Grid directly in the browser (Let me know if you are interested in this one in the comments below and I’ll do another post).

Good luck! If you have any questions, comments, or suggestions please feel free to leave a comment. I try to respond to all of them as soon as I can.




Want to Export Files?

Enter your email to get a bonus how-to guide that will walk you through exporting XLSX files from UI-Grid.

The post Easily Import Spreadsheets into UI-Grid appeared first on Brian Hann.

]]>
https://brianhann.com/easily-import-spreadsheets-into-ui-grid/feed/ 34 565
Make UI-Grid Take up The Whole Page https://brianhann.com/make-ui-grid-take-up-the-whole-page/ https://brianhann.com/make-ui-grid-take-up-the-whole-page/#comments Tue, 09 Jun 2015 19:45:59 +0000 https://brianhann.com/?p=441 Specifying heights in UI-Grid is a bit weird. Because a grid’s content is virtualized, you really have to tell the grid how tall you want it to be. If it has 100,000 rows in its data set, but you only want to see 20, or 400px worth, you have to tell it so. If you don’t give […]

The post Make UI-Grid Take up The Whole Page appeared first on Brian Hann.

]]>
Specifying heights in UI-Grid is a bit weird.

Because a grid’s content is virtualized, you really have to tell the grid how tall you want it to be.

If it has 100,000 rows in its data set, but you only want to see 20, or 400px worth, you have to tell it so. If you don’t give it a height then the grid will assume you want to see 10 rows, minimum.

Note that if you want to specify the height by giving a number of rows to show, you can use the minRowsToShow option.

If you want a deeper discussion, feel free to read A Hidden Grid Manifesto in the UI-Grid docs.

Full Page Grids

How about the situation where we want to make the grid take up the entire web page, height and width?

Luckily this can be done with just a little bit of CSS. We give the grid 100% of the page width, and then for the height we use a viewport percentage length. That’s where we specify the size of an element relative to the size of the viewport, and not relative to the element’s parent.

So if the viewport is 800px high and we say height: 100vh, our element will be 800px high. For the grid, in my experiments 98 looks about right, as 100 is a wee bit too big.

.grid {
  height: 98vh;
}

Example

You can see the result by opening this plunker. The grid takes up the whole viewport, and will resize nicely with the window. Great!




Sign Up

Like what you read? Sign up for more! I love sharing tips, tricks, and methods to make web development faster and easier. And believe me, I hate spam just as much as you do.

The post Make UI-Grid Take up The Whole Page appeared first on Brian Hann.

]]>
https://brianhann.com/make-ui-grid-take-up-the-whole-page/feed/ 3 441
UI-Grid and Row Animations https://brianhann.com/ui-grid-and-row-animations/ https://brianhann.com/ui-grid-and-row-animations/#comments Wed, 03 Jun 2015 16:44:29 +0000 https://brianhann.com/?p=408 When you add and delete rows in UI-Grid it can be a bit jumpy. All of a sudden, rows get shifted around and you’re not sure where you were. It’s like dropping a book on the ground and losing your place: Where the hell was I? In this post we’ll explore how to add animations […]

The post UI-Grid and Row Animations appeared first on Brian Hann.

]]>
When you add and delete rows in UI-Grid it can be a bit jumpy.

All of a sudden, rows get shifted around and you’re not sure where you were.

It’s like dropping a book on the ground and losing your place:

Where the hell was I?

In this post we’ll explore how to add animations to grid row operations to visually demonstrate what’s happening. The user should be able to tell what rows have just been added, and what rows are being removed.

Animating with CSS

CSS animations have been around a while. You might have used them on your own projects. If you haven’t, check out a couple links to see them in action.

In case you’re unfamiliar with the idea, animating with CSS offloads the hard work to the browser, which can optimize the operations and keep things looking smooth.

Animations also allow visual continuity. As the Google Material Design docs say:

Transitioning between two visual states should be clear, smooth, and effortless. A well-designed transition tells the user where to focus their attention.

Angular provides a module called ngAnimate which will automatically animate elements in directives like ng-repeat, ng-if, and ng-show if you provide the right CSS classes. Read about that in the Angular docs.

The Problem

Unfortunately there’s a problem when we want to use animations in UI-Grid. Because the grid’s content is virtualized, we are not adding and deleting DOM elements in a way that Angular can automatically manage.

Virtualization means that instead of rendering every row in your data set, the grid only renders what would be visible on the screen, plus a little bit extra as a hedge. Everything else is just wide empty space that you don’t see because you’re basically looking through a window at your data.

The Solution

To sum up so far, we fake having a big old data set on your screen, which means we can’t use the standard and easy animation method. How do we do the animations then? Fake them as well.

Angular’s  $animate service provides us with several methods for manually triggering animations. We’ll use two: addClass and removeClass. When you call these on an element Angular will apply some extra classes you can set up with CSS transitions.

We will fake the rows appearing and disappearing by watching for the data to change from within code that’s bound to the row. If the rendered row’s data changes to a new row, start the animation.

The animation itself for adding a row will set the row’s opacity to 0 initially, right when the data is changed, then it will transition to full opacity over one second. This will give the appearance of a new row fading in.

Let’s look at the CSS we’ll be using for adding rows:

.new-row {
  opacity: 0;
}

.new-row-remove {
  -webkit-transition: 1s linear all;
  -moz-transition: 1s linear all;
  -o-transition:1s linear all;
  transition: 1s linear all;
  opacity: 0;
}

.new-row-remove-active {
  opacity: 1;
}

The first class is the one we apply to new rows. We can do this with normal $elm.addClass('new-row'); this makes the row invisible initially. When we remove this class with $animate.addClass(element, 'new-row'), however, Angular will also first add the .new-row-remove class to start the animation, and at the end of the animation add the .new-row-remove-active class. Angular does this by parsing the styles the classes apply and pulling out the transition timing information. The result is that the row will transition from no opacity to full opacity over 1 second.

Enough talk, lets see the example:

Example

There’s a bit more here than we’ve talked about, but focus on the end result of clicking the “Add Rows” button. Remember that no elements are being created or destroyed. We’re just altering CSS and the data binding. So the rows appear to shift down, and the new rows appear to fade in. Best of both worlds, right?

Go ahead and play with the other settings. You can make the rows slide in, and alter the timing so it’s slow or fast. You can also change the easing function; see easings.net to see a bunch of different easing types in action.

Code for Adding Rows

We’ve seen the CSS. What about the JS? We said that we’d watch the data bound to the row and use $animate to change classes. Here’s what that looks like:

// In our grid options:
onRegisterApi: function (gridApi) {
  gridApi.grid.registerRowBuilder(function (row, gridOptions) {
    row.isNew = true;
  });
}

.directive('uiGridRow', function ($animate, $timeout) {
  return {
    priority: -1,
    link: function ($scope, $elm, $attrs) {
      $scope.$watch('row.entity', function (n, o) {
        if ($scope.row.isNew) {
          $elm.addClass('new-row');
          
          $timeout(function () {
            $animate.removeClass($elm, 'new-row');
          });
          
          $scope.row.isNew = false;
        }
      });
    }
  };
});

In our grid options we bind to the grid’s API and register what’s called a Row Builder. Row builders are functions that are executed on rows when they’re created. You can use them to extend row construction as you see fit. Here we just add a flag identifying the row as new.

We are stacking this directive on top of the existing uiGridRow directive. They will both run but ours runs at a lower priority so as not to affect the core directive.

In our link function we simply watch row.entity, which is the element from your dataset array. When it changed, we see if there’s an isNew property. If so, we add the new-row class, then remove it with $animate. Finally, we reset the isNew flag so the animation doesn’t run again during scrolling.

Q: Hey, in your $watch why aren’t you checking to see if n and o are unequal? Aren’t you running the animations too much?

Answer: No, not exactly. Every row initially has the isNew flag but on the initial $watch execution the entity hasn’t changed, so n and o are in fact equal. If we don’t allow the animation to run they when new rows are added the animations on ALL the visible rows will run at once, and that looks weird. Visually you don’t see the animations run as the row elements pop in when the grid starts, however.

Animated Row Deletions

The delete button on each row will perform the chosen animation, but backwards. We’ll use the delete-row class. Here’s the minimum amount of CSS:

.delete-row-add {
  -webkit-transition: 0.5s linear all;
  -moz-transition: 0.5s linear all;
  -o-transition: 0.5s linear all;
  transition: 0.5s linear all;
  opacity: 1;
}

.delete-row-add-active {
  opacity: 0;
}

So when the delete-row class is added, the row starts at opacity: 1 and over 0.5 seconds, transitions to opacity: 0.

The code for this is a bit different than what we’ve seen so far, however.  We have two problems:

  1. We can’t just remove the row when the button is clicked. We have to wait for the animation to run, THEN remove the row. Otherwise the row will just pop out of existence. The removeClass method returns a promise that is resolved after the animation completes so we can use that perform the removal.
  2. The scope isolation within the delete button’s grid cell prevents us from having access to our custom uiGridRow directive. We will need to emit an event from the cell that the row listens for.

Let’s see the code. Make sure to pay attention to the comments. They explain how the pieces work together.

// Stack the uiGridCell directive
.directive('uiGridCell', function () {
  return {
    priority: -1,
    link: function ($scope, $elm, $attrs) {
      // Expose the deleteRow function to the cell scope so our custom template
      //  can call it
      $scope.deleteRow = deleteRow;
      
      // When the delete button is clicked, emit a "delete-row" event and pass
      //   the row as an argument
      function deleteRow(row) {
        $scope.$emit('delete-row', row);
      }
    }
  } 
})

// In our stacked uiGridRow directive...
.directive('uiGridRow', function ($animate, $timeout, uiGridConstants) {
  return {
    priority: -1,
    link: function ($scope, $elm, $attrs) {
      // Listen for the above "delete-row" event
      $scope.$on('delete-row', function (evt, row) {
        // Start the animation for removing the row by adding the .delete-row class
        $animate.addClass($elm, 'delete-row')
          // ... the animation is done.
          .then(function () {
            // Remove the .delete-row class; we don't want any more animations to run.
            $elm.removeClass('delete-row');
            
            // We're not in $digest cycle currently, so we'll initiate a new one with $timeout.
            $timeout(function () {
              // Splice out the row from the data set. This will visually make it
              //   look like the rows shift up.
              var data = $scope.grid.options.data;
              data.splice(data.indexOf(row.entity), 1);
            });
          });
      });
    }
  }
});

That’s a bunch of lines but it gets us what we want. If you haven’t yet, check out deleting rows in the example above.

The rows fade or slide out and then the data shifts up. You’d never know the DOM elements were staying right where they are!

Where to Go From Here

What else can you do with animations, either in UI-Grid or your app? How can you help your users by providing meaningful transitions? What common operations do they (or you) find confusing?

Perhaps you could animate popping up modals, or filtering. Maybe transitions between sections of your application, or interactions with buttons, checkboxes and other elements. The sky’s the limit! Check out the Angular Material Demos to see how Google is encouraging the use of animations in web and mobile applications.

If you have any questions, please feel free to pop them in the comments below. I always respond as soon as I can. Also, for general UI-Grid talk join us on Gitter in the UI-Grid chat room. See you there!




Sign Up

Like what you read? Sign up for more! I love sharing tips, tricks, and methods to make web development faster and easier. And believe me, I hate spam just as much as you do.

 

The post UI-Grid and Row Animations appeared first on Brian Hann.

]]>
https://brianhann.com/ui-grid-and-row-animations/feed/ 6 408
UI-Grid and Dropdowns https://brianhann.com/ui-grid-and-dropdowns/ https://brianhann.com/ui-grid-and-dropdowns/#comments Fri, 22 May 2015 07:19:00 +0000 https://brianhann.com/?p=287 Dropdown-type widgets abound in the web development space. No matter what you call them: combobox, select box, multi-select, they all accomplish basically the same thing: selecting one or more items from many. Sure, a lot of them have extra bells and whistles like type-ahead, sorting, custom display, etc., but with all the extra comes extra […]

The post UI-Grid and Dropdowns appeared first on Brian Hann.

]]>
Dropdown-type widgets abound in the web development space.

No matter what you call them: combobox, select box, multi-select, they all accomplish basically the same thing: selecting one or more items from many.

Sure, a lot of them have extra bells and whistles like type-ahead, sorting, custom display, etc., but with all the extra comes extra pain.

You’ll find that pretty much none of the commonly-used dropdown tools have compatible APIs. Some have events they fire, some don’t. Some will let you extend them easily, others are very constrained.

When you combine all the different options out there with a need to put a dropdown in UI-Grid, the pain multiplies:

How do I get the dropdown to display right in the grid? How do I hook the dropdown up to my data?

And what if I spend a ton of time on this and it ends up not working?

I want to give you a quick fix that will work for a good number of your use cases.

In this post you’ll find a working example for using one of the popular dropdown widgets: UI-Select.

Why UI-Select?

Why use this one of all of the others? The short answer is that it can attach to the document body, rather than just inside its parent element. UI-Grid has to prevent visible overflow in a lot of places, which means that widgets that show potentially long lists (which dropdowns can do) can get cut off.

Placing the widget’s contents outside of the grid avoids that problem completely.

OK, So How Do I Use It?

We’ll cover a short series of steps to getting UI-Select working in our grid. For our example, we will have a bunch of data where each row is a user with a gender. When editing the gender we want to be able to select an option from a dropdown.

Here’s our steps:

  1. Set up the grid for editing
  2. Create a edit template for UI-Select, hooking it up to our grid rows
  3. Wrap our UI-Select template so we can handle events like hiding it when we click elsewhere on the grid or document

1. Grid Setup

Here’s what our grid options look like:

$scope.gridOptions = {
    rowHeight: 38,
    columnDefs: [
      { name: 'name' },
      { name: 'age', type: 'number' },
      {
        name: 'gender',
        editableCellTemplate: 'uiSelect.html',
        editDropdownOptionsArray: [
          'male',
          'female',
          'other'
        ]
      }
    ]
  };

In our gender column we specify a editDropdownOptionsArray option. This is normally used by the built-in HTML select box option, but we’ll re-purpose it for our custom dropdown. The values are simply the different genders we can choose.

You’re also not limited to a basic array of strings; your dropdown options could be complex objects as well:

editDropdownOptionsArray: [
  { id: 0, value: 'male' },
  { id: 1, value: 'female' },
  { id: 2, value: 'other }
]

You’d just need to make sure you bind to the right property to display the value.

Row Height

You’ll also notice that we set a custom rowHeight. This lets the dropdown widget fit nicely in our row without overlapping any of the borders. You could always resize it with CSS if you wanted to but this serves our purposes.

2. Edit Template

This is our editable cell template. We are using the “selectize” theme. UI-Select offers others but this one is nice and clean.

<ui-select ng-model="MODEL_COL_FIELD" theme="selectize"
    append-to-body="true">

  <ui-select-match placeholder="Choose...">{{ COL_FIELD }}</ui-select-match>
  <ui-select-choices repeat="item in col.colDef.editDropdownOptionsArray | filter: $select.search">
    <span>{{ item }}</span>
  </ui-select-choices>
</ui-select>

Note the append-to-body="true". That’s the trick to making the dropdown display right.

You’ll also see that we use two placeholders in our template: MODEL_COL_FIELD and COL_FIELD. The first one is used to bind the dropdown to the right property in our row entity.

The second is used to display the initial selected value in the dropdown when we double-click to edit.

We access the dropdown options we defined above by using the column’s column definition: col.colDef.editDropdownOptionsArray.

3. Wrap the Template

This template would work, but it’s got one problem: it won’t disappear when we click elsewhere on the page. The dropdown doesn’t know it’s supposed to hide when we want to interact with other elements.

We can fix this by wrapping the UI-Select directive in a custom directive where we’ll add some event bindings. We’ll call the directive ui-select-wrap and it will look a little something like this:

uiSelectWrap.$inject = ['$document', 'uiGridEditConstants'];
function uiSelectWrap($document, uiGridEditConstants) {
  return function link($scope, $elm, $attr) {
    $document.on('click', docClick);
    
    function docClick(evt) {
      if ($(evt.target).closest('.ui-select-container').size() === 0) {
        $scope.$emit(uiGridEditConstants.events.END_CELL_EDIT);
        $document.off('click', docClick);
      }
    }
  };
}

What we’re looking for here is catching any clicks on the entire document that don’t fall without the container that holds our active UI-Select element.  We do this by attaching a click handler to $document (which is just an Angular wrapper around the standard document object).

We use jQuery’s closest() method to see if the click target has a .ui-select-container ancestor. If we didn’t want jQuery as a dependency we could also do this using the gridUtil service’s closestElm method like so: gridUtil.closestElm(evt.target, '.ui-select-container');

If the click isn’t on the UI-Select widget we fire END_CELL_EDIT and detach our click handler. Done!

Example

Here’s a plunker demonstrating the code we wrote above. You can double-click on the gender fields to show the dropdown.

What About Other Widgets?

Like we said above, there’s many many other dropdown widgets out there.  Just to name a few:

There may be ways to get these working but you have to ask the question: “Will these attach to the document body?” If not, you’ll be fighting with getting the dropdown to display outside the grid and that’s no fun.

If you have any questions about getting UI-Select to work in your grid, or if you want to explore using other widgets in UI-Grid, leave me a message in the comments. I respond to all of them!




Sign Up

Like what you read? Sign up for more! I love sharing tips, tricks, and methods to make web development faster and easier. And believe me, I hate spam just as much as you do.

The post UI-Grid and Dropdowns appeared first on Brian Hann.

]]>
https://brianhann.com/ui-grid-and-dropdowns/feed/ 114 287
Customize UI-Grid with Dynamic Cell Classes https://brianhann.com/customize-ui-grid-with-dynamic-cell-classes/ https://brianhann.com/customize-ui-grid-with-dynamic-cell-classes/#comments Sun, 03 May 2015 20:47:13 +0000 https://brianhann.com/?p=340 In 6 Ways to Take Control of Displayed Data in UI Grid we looked at some common methods you can use to control how your data looks in UI-Grid. A grid is only as good as the control it gives over the data displayed in it, and the goal of UI-Grid is definitely to give you as much […]

The post Customize UI-Grid with Dynamic Cell Classes appeared first on Brian Hann.

]]>
In 6 Ways to Take Control of Displayed Data in UI Grid we looked at some common methods you can use to control how your data looks in UI-Grid. A grid is only as good as the control it gives over the data displayed in it, and the goal of UI-Grid is definitely to give you as much control as possible.

In this post we are going to look at a couple more tricks that will get you closer to the goal of perfect customization

Dynamic Cell Classes

When you specify your grid’s columnDefs you can give each column a cellClass. This can either be a string, or in the case we’re going to look at: a function.  The function will receive the grid, row, column, and row and column indexes as arguments. You just return the appropriate class name

Here’s an example:

$scope.gridOptions = {
  enableSorting: true,
  columnDefs: [
    { field: 'name' },
    { field: 'gender',
      cellClass: function(grid, row, col, rowRenderIndex, colRenderIndex) {
        if (grid.getCellValue(row ,col).toLowerCase() === 'male') {
          return 'red';
        }
      }
    }
  ]
};

You don’t have to worry about figuring out your binding if you have a complicated one. The Grid provides a getCellValue() function. If you pass the row and column in it will return the value for that row+col combination.

Be aware that each grid cell has its background color set with a class definition that looks like this: .ui-grid-row:nth-child(odd) .ui-grid-cell. If you give your grid a custom class, like .grid you can just define your custom cell class as .grid .ui-grid-row .ui-grid-cell which will raise its CSS specificity enough to take over.

Lastly here’s a plunker that demonstrates what this looks like:




Want even more control over UI-Grid?

There's a new video series coming out tell you how to write plugins with the UI-Grid Plugin Generator. Sign up and be notified when the first video is out!

The post Customize UI-Grid with Dynamic Cell Classes appeared first on Brian Hann.

]]>
https://brianhann.com/customize-ui-grid-with-dynamic-cell-classes/feed/ 14 340
Write Your Own UI-Grid Plugin https://brianhann.com/write-your-own-ui-grid-plugin/ https://brianhann.com/write-your-own-ui-grid-plugin/#comments Tue, 21 Apr 2015 21:45:40 +0000 https://brianhann.com/?p=299 There are a lot of third-party modules out there for Angular, and even more plugins and extensions for those modules. But even with such a great ecosystem, you won’t always find the functionality that you need. If you’ve done web development for any length of time I’m sure you’ve had to write your own extensions, or […]

The post Write Your Own UI-Grid Plugin appeared first on Brian Hann.

]]>
There are a lot of third-party modules out there for Angular, and even more plugins and extensions for those modules.

But even with such a great ecosystem, you won’t always find the functionality that you need.

If you’ve done web development for any length of time I’m sure you’ve had to write your own extensions, or modify someone’s module to fit your needs. Writing those things from scratch can be a real bear, much less adding things like tests or a reliable build system.

And what if you want to share your finished product with others? It’s probably twice as much work if you want nice, clean documentation and usable demos.

I wanted to lower the barrier of entry for you and get you on your way to writing any UI-Grid plugin you want with a minimum of fuss. I would bet that you (yes you) have some great ideas. Why should you have to spend a ton of time building all the little non-plugin parts around your plugin before you actually build and share your plugin?

Answer: you don’t have to.

UI-Grid Plugin Generator

Meet the UI-Grid Plugin Generator: a simple Yeoman generator that you can use to create the base of your plugin. What’s it got? I’m glad you asked!

  • Runs on Gulp.
  • Complete build tool chain with js and less contatenation, minification, sourcemaps, and banners.
  • Angular-safe minification with ngAnnotate.
  • Keeps your code clean with JSHint and JSCS.
  • Automated testing with Karma.
  • Bundles your html templates with ngHtml2js.
  • Automated documentation and demo creation with Dgeni.

The last one is a killer. Don’t worry about building your plugin’s website from scratch. Just document your code, write a few tutorials, and Dgeni will generate a complete fully-functioning site that’s similar to UI-Grid’s. This has saved the UI-Grid team a ton of time, and is used in major projects like Angular itself, Ionic, and Angular Materials.

What about Feature X?

This project is brand new, so there’s a few things missing that will be added soon. The main hangup is Protractor. There is no e2e testing yet as it’s not working, but it will be fixed soon.

A couple other features on the roadmap are:

How Do I Get Started?

Here’s all the steps:

  1. Install the Yeoman generator (and Yeoman if you don’t have it already)
  2. Generate your plugin boilerplate.

Two steps, not bad right? Here’s what it looks like:

npm install -g yeoman generator-ui-grid-plugin
mkdir my-plugin && cd my-plugin
yo ui-grid-plugin

The generator will prompt you for your plugin name and some other information.

You can provide a Google Analytics tracking code if you want it embedded on your plugin’s site.

How Do I Publish?

The application is set up so that your Travis-CI takes your successful builds and creates a documentation & demo website with GitHub pages. Follow these here steps:

  1. Sign up for Travis: follow steps 1 and 2 here.
  2. Create a GitHub token for Travis to use. You can do that on your GitHub settings page. The token should only need access to your public repos. Make sure to copy your token and keep it safe. It can only be seen once so if you lose it you’ll have to generate a new one.
  3. Encrypt your token with the travis Ruby gem. There’s more detailed instructions on the project’s homepage.

Customization Ideas

Here are some ideas:

  • Use SASS instead of less for CSS compilation.
  • Plug in a bootstrap theme to make the site look more individual. You might look: here, here, or here.
  • Mash-up your plugin with other cool Angular modules, like UI Tree or ngStorage.
  • Tinker with Dgeni to add more advanced docs generation.

If you want to watch a tutorial video series on creating a fully-functioning plugin, sign up below and you’ll receive a link when it’s ready.




I'm interested!

Send me a link to the video tutorials when they're done.

The post Write Your Own UI-Grid Plugin appeared first on Brian Hann.

]]>
https://brianhann.com/write-your-own-ui-grid-plugin/feed/ 3 299
UI-Grid: The Easiest Customization You’ll Ever Write https://brianhann.com/ui-grid-the-easiest-customization-youll-ever-write/ https://brianhann.com/ui-grid-the-easiest-customization-youll-ever-write/#comments Tue, 07 Apr 2015 21:07:22 +0000 https://brianhann.com/?p=208 Currently UI-Grid doesn’t automatically show any messages when data is loading. Nor does it show anything when there’s no data to show. This can be a bit confusing for your end users as all they’ll see is an empty white space in the grid. Luckily this is easy to fix with the most misunderstood feature […]

The post UI-Grid: The Easiest Customization You’ll Ever Write appeared first on Brian Hann.

]]>
Currently UI-Grid doesn’t automatically show any messages when data is loading.

Nor does it show anything when there’s no data to show.

This can be a bit confusing for your end users as all they’ll see is an empty white space in the grid.

Luckily this is easy to fix with the most misunderstood feature of Angular 1.2: transclusion.

Bonus: Want to see the advanced use cases in the image, like selecting data by clicking on a map and loading data from a file the user drags and drops on to the grid? Enter your email and receive a link to a special resource

Angular is a Donut

Angular transclusion is a jelly donut

Transclusion is delicious.

Think of transclusion as a delicious jelly donut. UI-Grid is the donut and the functionality you want to add is the jelly. Is your functionality raspberry flavored? Never mind; don’t answer that.

In addition to simply being inside the donut and available for consumption, your jelly filling has access to all the features and properties of the donut. Like the frosting or sprinkles, if there are any. Alright so maybe the metaphor breaks down there. The point is that your transcluded will have a scope that is a descended of your grid’s scope, so you can access the grid property and all of its settings and data in your expressions.

What does transclusion look like? Not all that much:

<div ui-grid="gridOptions" class="grid">
  <div>I'm a special little transcluded div for grid {{ grid.id }}!</div>
</div>

The div inside your ui-grid element is what’s being transcluded. Pretty simple, right?

Alright enough theory, let’s get started. You and I are going to look at two use cases:

  1. A “Loading…” message with a spinner indicator
  2. A “No Data” message

Loading Message

This message will be an overlay that will sit on top of the grid while your data is loading. The markup is short and sweet, just a couple divs and your message. We will use an ng-hide on the overlay that will only let it show if we are loading and have no data.

<div ui-grid="gridOptions" class="grid">
  <div class="no-data" ng-hide="gridOptions.data.length && !loading">
    <div class="msg">
      <span>Loading Data...</span>
    </div>
  </div>
</div>

Our controller code is pretty short as well. We’ll make an init function that is called when the controller starts up. That will set a loading flag on the scope, and then unset it in our data fetch’s finally handler.

app.controller('MainCtrl', function ($scope, $http) {
  $scope.gridOptions = {};

  init();

  function init() {
    $scope.loading = true;

    $http.get('names.json')
      .success(function (data) {
        $scope.gridOptions.data = data;
      })
      .finally(function () { $scope.loading = false; })
  }
});

Alright, if we fire this up what are we going to see?

ui-grid loading message image 1

This is a non-interactive image of what the user would see.

Hmm, that’s not quite right. It looks like any content we transclude will just be appended to the grid element’s contents. I guess we’ll need some custom CSS to position it correctly and make it look nice. We want the overlay to:

  1. Overlay the entire grid
  2. Darken the grid a bit with some opacity
  3. Generally look nice
  4. Have an animated progress widget

Here’s the CSS for all that. Read the comments for what it all does.

/*
   This is the background of our overlay. We need it to be
   absolutely positioned within the grid, and fill from
   top to bottom, and the full width. It will also have
   a black background with 40% opacity.
*/
.grid-msg-overlay {
  position: absolute;
  top: 0;
  bottom: 0;
  width: 100%;
  background: rgba(0, 0, 0, 0.4);
}

/*
  This guy will contain our message. We want it centered
  so it's positioned absolutely with percentage-based
  offsets and dimensions. It also has some basic border
  stuff and most important is using "display: table" so
  we can vertically center its contents.
*/
.grid-msg-overlay .msg {
  opacity: 1;
  position: absolute;
  top: 20%;
  left: 20%;
  width: 60%;
  height: 50%;
  background-color: #eee;
  border-radius: 4px;
  border: 1px solid #555;
  text-align: center;
  font-size: 24px;
  display: table;
}

/*
  Lastly this is the actual message text. It uses
  display: table-cell so the vertical alignment
  works properly.
*/
.grid-msg-overlay .msg span {
  display: table-cell;
  vertical-align: middle;
}

Note on Vertical Centering: This is beyond the scope of this post, but there’s a bunch of different ways to vertically center elements. I’m using table CSS here, which might not be the best for you. Check out this Chris Coyier post for more.

Our result is much better! Let’s take a look at a demo of what we’ve just done:

OK, looking good. But what if nothing loads and our users are still staring at an empty grid? Since we’ve got Let’s add a “No Data” message.

No Data, No Problem

We already have the CSS set up so all we need is some markup:

<div ui-grid="gridOptions" class="grid">
  <div class="no-data" ng-hide="gridOptions.data.length && !loading">
    <div class="msg">
      <span>Loading Data...</span>
    </div>
  </div>
  <div class="no-data" ng-hide="!gridOptions.data.length && loadAttempted">
    <div class="msg">
      <span>No Data</span>
    </div>
  </div>
</div>

Then we can add the loadAttempted flag to the finally handler:

.finally(function () {
  $scope.loading = false;
  $scope.loadAttempted = true;
});

Here’s our resulting plunker:

Et voila! We are done.  Transclusion is great for when you just want some simple extensions. Would you like more examples, such as:

  • Selecting data for UI-Grid by selecting from a map of the US
  • Loading message while using external paging
  • Drag-and-drop file input for loading custom JSON data into grid

Enter your email below and I’ll send you a link to our bonus area where you can these uses cases for transclusion in UI-Grid, with example demos+code and explanatory documentation. Enjoy!




Sign up

Subscribe to receive access to the bonus resource: Advanced UI-Grid Customization with Transclusion

The post UI-Grid: The Easiest Customization You’ll Ever Write appeared first on Brian Hann.

]]>
https://brianhann.com/ui-grid-the-easiest-customization-youll-ever-write/feed/ 14 208
Create a Modal Row Editor for UI-Grid in Minutes https://brianhann.com/create-a-modal-row-editor-for-ui-grid-in-minutes/ https://brianhann.com/create-a-modal-row-editor-for-ui-grid-in-minutes/#comments Sat, 28 Mar 2015 21:20:16 +0000 https://brianhann.com/?p=162 UI-Grid has editing features built in. But sometimes you want to edit data all at once. And add some custom validation, third-party controls, and so on. Normally if you wanted to create your own row modal editor it would mean wiring up a modal service like the one from UI Bootstrap, writing your own form out by […]

The post Create a Modal Row Editor for UI-Grid in Minutes appeared first on Brian Hann.

]]>
UI-Grid has editing features built in.

But sometimes you want to edit data all at once.

And add some custom validation, third-party controls, and so on.

Normally if you wanted to create your own row modal editor it would mean wiring up a modal service like the one from UI Bootstrap, writing your own form out by hand, including the validation and controls.

It can take a lot of time to write up a form for all your little data points, and let’s be honest, it’s pretty boring. There’s only so many times I can type in <input type="text" /> (even with an HTML generator) before I’m sighing resignedly and shifting in my seat.

In this post I’m going to show you how to short-circuit this routine and create a row editor for UI-Grid in just minutes with a great module called Angular Schema Form. You won’t have to write up any form controls, validation, or save/cancel behavior. The only thing you have to supply is the configuration, and Angular Schema Form will take care of the rest.

Making Your Rows Editable

The first thing we need to do is some basic hook-ups to open a modal to edit our rows. To do this we’ll add a little button as the first column of our grid. For the modal we’ll use UI Bootstrap’s $modal service, and Font Awesome for icons.

These are our column defs, we’ll use edit-button.html as our custom button template.

columnDefs: [
  { field: 'id', name: '', cellTemplate: 'edit-button.html', width: 34 },
  { name: 'name' },
  { name: 'company' },
  { name: 'phone' },
  { name: 'City', field: 'address.city' },
]

And here’s the cell template:

<div class="ui-grid-cell-contents">
  <button type="button"
     class="btn btn-xs btn-primary"
     ng-click="grid.appScope.vm.editRow(grid, row)">
    <i class="fa fa-edit"></i>
  </button>
</div>

Notice that the ng-click is firing off a function bound to something called grid.appScope. UI-Grid uses isolate scope, which means that it does not inherit from the scope of its ancestor elements; so if you stick your grid inside an element that has a controller on it, you can’t just access your controller’s scope directly. To help you, UI-Grid binds its parent scope to grid.appScope. To give you an idea here’s what part of our controller would look like:

MainCtrl.$inject = ['$modal'];
function MainCtrl($modal) {
  var vm = this;

  vm.editRow = editRow;
  vm.gridOptions = {
    columnDefs: [
      { field: 'id', name: '', cellTemplate: 'edit-button.html', width: 34 },
      { name: 'name' },
      { name: 'company' },
      { name: 'phone' },
      { name: 'City', field: 'address.city' },
    ]
  };
  
  init();

  //////////// 
  
  function editRow(grid, row) {
    $modal.open({
      templateUrl: 'edit-modal.html',
      controller: ['$modalInstance', 'grid', 'row', RowEditCtrl],
      controllerAs: 'vm',
      resolve: {
        grid: function () { return grid; },
        row: function () { return row; }
      }
    });
  }
  
  function init() {
    $http.get('http://ui-grid.info/data/500_complex.json')
      .success(function (data) {
        vm.gridOptions.data = data;
      });
  }
}

There’s our editRow function, which we expose by binding it to our controller. If we declare our controller like ng-controller="MainCtrl as vm" then we’ll have the vm property available on grid.appScope to use in our button template. RowEditCtrl will be the controller for the modal which we’ll define later. The editRow function will take the grid and row as arguments and we’ll inject them into RowEditCtrl as locals with resolve. You can read about all this in the UI Bootstrap modal docs.

The Modal

The modal is just going to be a super simple bootstrap template, with a form and save and cancel buttons:

<div>
  <div class="modal-header">
      <h3 class="modal-title">Edit Row</h3>
  </div>
  <div class="modal-body">
    <form sf-schema="vm.schema" sf-form="vm.form" sf-model="vm.entity"></form>
  </div>
  <div class="modal-footer">
      <button class="btn btn-success" ng-click="vm.save()">Save</button>
      <button class="btn btn-warning" ng-click="$close()">Cancel</button>
  </div>
</div>

What’s that sf-schema stuff? I’m glad you asked!

Angular Schema Form

Angular Schema Form is a directive that takes a few options and generates a form with inputs bound to the data that you specify.

First there’s the schema. This is a JSON Schema standard object that defines the data in your entity: their type (string, number, boolean, array, etc), title, description, and so on.

Defining Our Schema

Since we know what columns on grid is going to have, we can define our schema in a constant that we can inject in our app anywhere we want:

.constant('PersonSchema', {
  type: 'object',
  properties: {
    name: { type: 'string', title: 'Name' },
    company: { type: 'string', title: 'Company' },
    phone: { type: 'string', title: 'Phone' },
    'address.city': { type: 'string', title: 'City' }
  }
})

Notice the type: 'object' and properties that we use to set up the root of our schema. Don’t forget this or your form will not work with no errors and you’ll be scratching your head like I did.

All our elements are pretty straight-forward. The only exception is the city, which is a slightly more complex binding. Angular Schema Form uses objectpath library to determine bindings so there’s some options if you have nested properties you want to access.

Form Setup

Remember those sf- attributes on our form in the modal template? Well the first one, sf-schema, gets bound to the schema we just created. sf-model is bound to the object we want to edit, in this case our row, but if we bind directly to the row we will be updating it live, which can be nice sometimes but it would be good to have a “cancel” feature. Let’s do that.

function RowEditCtrl($modalInstance, PersonSchema, grid, row) {
  var vm = this;
  
  vm.schema = PersonSchema;
  vm.entity = angular.copy(row.entity);
  vm.form = [
    'name',
    'company',
    'phone',
    'address.city'
  ];
  
  vm.grid = grid;
  vm.row = row;
  vm.save = save;
  
  function save() {
    // Copy row values over
    row.entity = angular.extend(row.entity, vm.entity);
    $modalInstance.close(row.entity);
  }
}

Alright, our modal controller is going to get $modalInstance, which the $modal service injects for us. We can use that to interact with the modal (closing it and so on). We’ll provide the PersonSchema, copy our row entity so we’re not directly editing the row, and then the next property is form. What’s that?

We’ve given our form a schema, which tells it what properties to expect, but how about what order they come in, or which ones we want to edit in this form?  If we didn’t really care we could do vm.form = ['*'], and then in our &lt;form&gt; do sf-form="vm.form". The ['*'] tells Angular Schema Form to just use all the properties from the schema, in whatever order the come out in. The problem with this is our address.city property won’t show up correctly. We have to explicitly tell the form what to expect, and since we have to tell it that we have to tell it all of them.

There’s lots of options for defining your form with Angular Schema Form: you can specify input types (text, radio, select, button groups), do hierarchical lists, tabs, conditional visibility, etc. There’s good examples on the project site.

The only thing left is the save function which copies over our updated row entity and closes the modal. Tada! You are the proud owner of a modal editor for UI-Grid which you can update whenever you want with only a couple lines. Almost painless, right?

Demo

Here’s a plunker showing it all working together.

Note: plnkr.co has been a little wonky about not loading fully sometimes; you may have to refresh it.

Et Fin

I hope you enjoyed this guide, and I’d like to help you out some more!  Next time we’ll walk through creating a UI-Grid plugin that will let us reuse this modal row editor code and create them wherever we want in just seconds. We’ll talk about Angular development best practices and use a plugin skeleton for creating tests, automatically generating documentation, and all that fun stuff.




Sign Up

Sign up here and I'll send you more tips, tricks, and shortcuts like this. I hate spam MORE than you and promise to only send things that will make your life better.

The post Create a Modal Row Editor for UI-Grid in Minutes appeared first on Brian Hann.

]]>
https://brianhann.com/create-a-modal-row-editor-for-ui-grid-in-minutes/feed/ 30 162
6 Ways to Take Control of Displayed Data in UI Grid https://brianhann.com/6-ways-to-take-control-of-how-your-ui-grid-data-is-displayed/ https://brianhann.com/6-ways-to-take-control-of-how-your-ui-grid-data-is-displayed/#comments Mon, 16 Mar 2015 20:09:15 +0000 http://104.131.219.158/?p=71 Getting your data displayed just right in UI Grid can be a huge pain. You might just want to format some numbers, or you might want to embed something complex like a chart or custom directive. In this post outline six (plus one) different methods you can use to get your data just the way […]

The post 6 Ways to Take Control of Displayed Data in UI Grid appeared first on Brian Hann.

]]>
Getting your data displayed just right in UI Grid can be a huge pain.

You might just want to format some numbers, or you might want to embed something complex like a chart or custom directive.

In this post outline six (plus one) different methods you can use to get your data just the way you want it:

  • Bindings
  • Update: Cell Classes
  • Cell Filters
  • Cell Templates
  • Links
  • Buttons
  • Custom directives

Bindings

UI Grid columns can be bound to any sort of Angular expression, as well as property names that would normally break an expression:

var columnDefs = [
 { field: 'name' },
 { field: 'addresses[0][0][1]' },

 /*
 Yes, you can use hyphens, plus signs, etc.

 Normally something like {{ row.entity.first-name }} would
 bomb but UI Grid pre-processes your bindings to make sure they work correctly.
 */
 { field: 'first-name' },

 /* Function expressions work too */
 { field: 'getCurrency()' },
 { field: 'transformValue(row.entity.myField)' },
];

Cell Classes

When you specify your grid’s columnDefs you can specify a CSS class for that column with the cellClass property. The simplest type is a just a string representing the CSS class name, though you can also specify a function if you want dynamic control.

var columnDefs = [
 { field: 'name' },
 { field: 'address', cellClass: 'address' }
];

Cell Filters

Cell filters can transform the displayed value for a column while leaving the model intact. In this plunker the amount column contains floating point values, but we only want to display the integer part. A simple cellFilter that uses .toFixed() will alter the displayed value the way we want.

Passing Arguments

In the third column I am using two different fields. The column itself is bound to the same field as the second column, but on the filter I am passing the scope like so: cellFilter: 'currencyFilter:this'. Using the this argument on your filter will pass the scope.

Then I can lookup the currency symbol I want using the currency field from the row.

If you double-click a cell in the Currency column you’ll see that the raw value is still available to be edited. Cell filters only change your displayed value!

Cell Templates

Column definitions take a cellTemplate argument you can use to give your cells a custom template. You can specify it a few different ways, with a url (relative or absolute), an Angular template ID, a string/Angular element, or a promise.

var columnDefs = [
  { field: 'name', cellTemplate: 'name-template.html' },
  { field: 'name', cellTemplate: 'myTemplateId' },
  { field: 'name', cellTemplate: $.get('url-to-your-template.html') }
];

Bindings in Cell Templates

There are a couple options for your binding your row’s data in your template. You can access the row object which is in the local scope; row.entity will contain the reference to your object, so if you want the “name” field, you can bind like so: {{ row.entity.name }}.

If you’ve already defined your binding in your column definition you can use one of UI Grid’s placeholders: {{ COL_FIELD }}. UI Grid will automatically replace COL_FIELD with the appropriate binding. If you need to two-way bind to your row, like when you’re using the edit feature, you’ll need to use the MODEL_COL_FIELD placeholder.

var columnDefs = [
  {
    field: 'image_url',
    cellTemplate: '<div class="ui-grid-cell-contents"><img src="{{ COL_FIELD }}" /></div>'
  }
];

Links and Buttons

Links are simple, just use regular old HTML. Wrapping your cell in an element using the class .ui-grid-cell-contents will apply the proper CSS settings like padding, overflow, etc., and make sure your cell fits in its space nicely.

var columnDefs = [
  {
    field: 'email',
    cellTemplate: '<div class="ui-grid-cell-contents"><a href="mailto:{{ COL_FIELD }}">Send E-Mail</a></div>'
  }
];

In the plunker below I’m using a basic cellTemplate to bind both the first and last name fields into one column. On the second column I’m using a simple link like above (note: none of these emails are real).

On the third one, I’m using a template that’s referenced in index.html. If you check the bottom of that file you’ll see the template inside a &lt;script type="text/ng-template"&gt; tag. Any tags of that type will put automatically put into the template cache ($templateCache) by Angular, with the id you specify.

Also in the third column template you can see I’m using a variable called grid.appScope. Anywhere in your templates, grid.appScope will be bound to the parent scope of your grid. I use it to bind my button to a function in my controller that will open a new window with a url to google maps with the address in it (note: most if not all of these address will not exist).

Tooltips

Tooltips like those from UI Bootstrap are a little bit different because they float over your content. By default the grid’s cells are set to hide any overflow, so if you just pop a tooltip in there it won’t show up and you’ll probably be very confused. You can solve this by either adding some custom overflow CSS to your cellTemplate, or if you’re using UI Bootstrap you can add tooltip-append-to-body="true" to the element with your tooltip and the tooltip will be appended to the body and absolutely positioned where it needs to be.

Hover over the names in the plunker below to see tooltips with overflow CSS; the third column uses the append-to-body method:

Tooltips cannot overflow beyond the constraints of the viewport, so they won’t show above the first row or below the bottom row, for example.

Custom Directives

You can put absolutely anything in your cell templates, just remember to use .ui-grid-cell-contents if you’re not applying your own custom cell CSS.

Here I’ve used d3.js, nvd3.js, and angular-nvd3.js to create sparkline charts in my cells.

Summin’ it up

You should be able to do the vast majority of what you want with your data using these six methods. If you have questions, suggestions, or just want to say hi, drop in to our gitter channel we’d love to meet you!

If you want some help navigating through these different customization methods, enter your email below to get a flowchart PDF showing you which method to choose for a given need.




Free UI-Grid Flowchart

The flow chart Which UI-Grid Customization Should I Choose? will help you pick the customization method best suited for your situation.

Enter your email address and you'll receive a link to the Bonus Area with the flowchart PDF and other helpful resources.


The post 6 Ways to Take Control of Displayed Data in UI Grid appeared first on Brian Hann.

]]>
https://brianhann.com/6-ways-to-take-control-of-how-your-ui-grid-data-is-displayed/feed/ 85 71