Quantcast
Channel: Damyan Petev's Blog
Viewing all 45 articles
Browse latest View live

jQuery Editors: How to get started and improve productivity and experience

$
0
0

jQuery EditorsThe Infragistics jQuery package comes complete with a range of editor widgets to help with one of the most common tasks  - gathering information from the user and making sure it is the one you need (validating it). Part of the product from day one – speaks just how essential they are. And in this blog and probably some more to follow  I’ll do my best to get you started and show ways to improve the editing experience and overall productivity, along with some tips and tricks. Collecting and filtering user input is one of those basic functionalities many platforms require and the jQuery version follows the path set by the type of experience provided by say ASP.NET Editors or XAML Inputs.

However, due to having both a client and server, web-based applications offer two separate points of opportunity to narrow down the unwanted input. So in such case the best place to start filtering input is on the client and the editors are ideal for that. More or less like the ASP.NET AXAJ counterpart the jQuery editors provide a similar set of controls – here’s the list:

  • Numeric editor, itself extended by:
    • Currency editor
    • Percent editor
  • Mask editor
  • Date editor
    • Date Picker
  • Text Editor

 

Before we dive into those, however, should be explained that all of them extend the base Editor class and use a Validator. Any extended editor can be created using the base Editor, should you need to, and any of the properties of the extended editors can be can be set within the igEditor. Of course, the whole point of the separate editors is to provide a mostly ready to use control with all the right defaults. So  further below you will see a logical representation of the editors hierarchy with short descriptions to help you figure out which one is the right for the situation.

jQuery Editors' hierarchy and descriptions

The editors’ most basic functionality is, well… to allow a text field to be edited, not surprisingly, and to provide support for additional buttons for dropdown and such. However, the actual validation that is often a huge part of the process comes courtesy of the igValidator. Yup, it’s a separate widget which means you can use it stand-alone if you will on other Input, Textarea or Select elements. The editors use those for main element and initialize validators for you, of course. In the interest of having a complete piece of information, it’s probably worth mentioning the Validator is also used by the jQuery Combo and Rating controls.

Getting started

Resources

First things first – some resources are ”required: true”, so to speak. Nothing too complicated though, but if you are not familiar with how resources can be handled your first stop should be our Deployment Guide and more specifically the page discussing how to work with JavaScript resources. The most basic resource to add are the editors themselves and the validator:

  1. $.ig.loader({
  2.     scriptPath: "http://cdn-na.infragistics.com/jquery/20121/2049/js",
  3.     cssPath: "http://cdn-na.infragistics.com/jquery/20121/2049/css",
  4.     resources: "igEditors,igValidator"
  5. });

or using the MVC helpers:

  1. @using Infragistics.Web.Mvc;
  2. @(Html.Infragistics().Loader()
  3.     .CssPath("http://cdn-na.infragistics.com/jquery/20121/2049/css")
  4.     .ScriptPath("http://cdn-na.infragistics.com/jquery/20121/2049/js")
  5.     .Resources("igEditors,igValidator").Render())

Editors

When instantiating the editors using just script you have to provide the actual markup (that being a container element or the actual input(s) to be transformed), for example:

  1. <inputid="date"/>

and then you can initialize the widget like:

  1. $("#date2").igDatePicker({
  2.     inputName: 'date',
  3.     required: true,
  4.     validatorOptions: { onsubmit: true }
  5. });

or using the MVC helpers(they will create all markup for you):

  1. @(Html.Infragistics().DateTimeEditor().InputName("date")
  2. .ValidatorOptions(validate => validate.OnSubmit(true))
  3. .Required(true).Render())

Of course, you can add quite a few more tweaking to those, but for now we are going for simple.

Getting values

To get actual values when you are submitting a form(see below or one for the demos for more complete layout) we include the additional ‘Input Name’ option. This has nothing to do with naming your input, even though the actual ‘name’ attribute of inputs is used by the browser when sending form data to the server. The option instead creates an additional field like this:

  1. <inputname="date"type="hidden"/>

The reason is to separate the representation (the editor’s displayed value can be different – date formats, masks, literals, etc.) from the actual value – the editor creates this field and automatically updates its value to match the editor’s and this way you get the right data on the server. And the ‘onsubmit’ validation will prevent sending the form if the editor’s value is empty!

In the demo I have two date editors and the is the action in the ‘Home’ controles handling the form submit:

  1. [HttpPost]
  2. publicActionResult Update(DateTime? date, DateTime? date2)
  3. {
  4.     /* use date and date2 here - e.g. perform additional checks
  5.        and save the input to database, etc. */
  6.     ViewBag.Message = "Entered date: " + date.ToString() +
  7.         ", and date2: "  + date2.ToString();
  8.  
  9.     //return to the same view with the new message
  10.     var path = Request.UrlReferrer.AbsolutePath;
  11.     return View(path != "/" ? path.Split('/').Last() : "Index");
  12. }

Of course, MVC does most of the value retrieval for you and some other helpful checks. One way or another this can be done on any platform!

Improve your pages’ input experience

Say you have a very common simple form for the user to fill in a few blanks. You can rely entirely on checking the result on the server but that is highly ineffective due to the completely uncontrolled user input and the additional round-trips to the server and back again. Then again as this is no new issue the HTML5 has added a whole plethora of input types to really ease that task and it’ll all be good… but none of the special features would work on IE8 or 7 and can be quite the show-stopper(not to mention the the patter to validate against in not supported at all in IE and Safari yet). So say you have the most simple form like so:

  1. formaction="@Url.Action("Index")"method="post">
  2.     <fieldset>
  3.         <legend>Editorslegend>
  4.             <label>First  name:label>
  5.             <inputname="name"/>
  6.             <br/>
  7.             <label>Phone: label>
  8.             <inputname="phone"/>
  9.             <br/>
  10.             <label>Email: label>
  11.             <inputname="email"/>
  12.             <br/>
  13.             <label>Description: label>
  14.             <br/>
  15.             <divid="descr">div>
  16.     fieldset>
  17.     <inputtype="submit"value="Send"><inputtype="reset">
  18. form>

It doesn’t looks  or do anything special, really:

Simple HTML Inputs

By all means, add all the HTML5 types to those, just keep in mind they will all fallback to simple text inputs for non-HTML5 browsers. As it has been a practice, the only reasonable solution is to rely on script validation on the client. Of course, you have the freedom and options to use anything. You can even attach an igValidator to those fields and get awesome results… OR simply turn those inputs into proper jQuery editor widgets. And it’s really simple too:

  1. $.ig.loader(function () {
  2.     $("input[name='name']").igMaskEditor({
  3.         inputMask: ">L>LL????????????????",
  4.         validatorOptions: {}
  5.     });
  6.  
  7.     $("input[name='phone']").igMaskEditor({
  8.         inputMask: "(000)0000 999",
  9.         validatorOptions: {}
  10.     });
  11.  
  12.     $("input[name='email']").igTextEditor();
  13.  
  14.     $("#descr").igTextEditor({
  15.         textMode: "multiline",
  16.         nullText: "Leave a note."
  17.     });
  18. });

Basically you don’t need to change you markup at all in most cases (it’s  still the same HTML only swapped the description Input for a DIV so the editor can render its own main element instead). The user editing and input experience, however, is now changed drastically:

HTML inputs transformed into jQuery Editor widgets

And by adding a single line with validator options ‘ validatorOptions: {}’ (can be empty for Mask Editors as their masks define required fields) will now also display an appropriate message when input doesn’t meet requirements:

One of the validation messages the jQuery Editors show the user.

The editors become the first line of filtering out inappropriate user input – preventing it from making a travel to the server and back, when it’s guaranteed to be rejected there. Save the server some work, help reduce pointless traffic and help the user by hinting the required input with mask prompt chars and messages.

Demos and resources

  • You fiddle with two demos of your choosing:

- A JSFiddle demo, where the submit button is only good for triggering validation.

- An ASP.NET MVC project showing both delimiting in script and using the Helper as well as the from summation handling in action. Keep in mind the MVC helpers do require the Infragistics MVC assembly, see below on how to get yourself one.

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Summary

As you’ve seen above the jQuery Editors are not rocket science to use, but can truly make a difference:

  • Filter input, reduce server load/rejection rate and redundant traffic
  • Enhanced editing experience
  • User-friendly messages
  • Completely customizable
  • High control
  • Rich API
  • Stylable
  • ThemeRoller compatible

And when you consider the benefits of the being able to use the editors regardless of HTML5 support, even on older IE versions and the wonderful capability of the MVC helpers to create validation based on your model’s MVC Data Annotations– you get a whole lot of functionality with little to no effort. Stay tuned for more jQuery Editors content coming very soon.


jQuery Editors: How to better guide users to the desired input

$
0
0

jQuery Editors input - control and guideBeing client-side controls I consider the editor widgets to have two major responsibilities  - take some of the load of the server by preventing invalid user input from ever reaching it (various performance enhancements)and at the same time do their best to filter/guide or even ‘enforce’ proper data entry (experience). Sounds familiar? That’s in the article in which I showed you how you can get started with the jQuery Editors and with that benefit from improved productivity and experience.

So the question is why would you have to guide user input in the first place?

I mean.. consider this - as with most web distributed systems (unless you are using your own communication protocols), you can never ever completely trustanything that comes from the ‘client’. And I’ll explain myself some more on this. The jQuery editors can’t really put an end to all the undesired input, JavaScript can only do as much. Plus, user agents provide consoles and script/markup manipulation right on the spot. Most defenses of such sort can be disregarded with great ease, really. You could go on and and make attempts to make it secure, but then again web requests are not exclusive to just browsers. And this is just intentional providing incorrect input (attempting hacking your app, put simply), however, even if you completely trust you users and say go as far as require client certificates with encryption.. well you never know when someone is going to hit the submit button after realizing he had made a typo or do so completely unaware the input is not supposed to be like that. Bottom line – you still have to do full validation of the received data on the server. So that’s part of the productivity gains from the editors that possibly applies in 99% of the time.

Now after that huge diversion I consider the productivity gains to be the smaller part of the benefits and the larger being the experience. Because what’s possibly more important is that in that 99% of the time the input can be controlled, filtered, hinted and a user won’t need to wait for a page reload to get a server message he did something wrong. And this includes all the filtering that some of the editor types do by default, the awesome masks for the igMaskEditor and the oh-so very flexible regular expressions supported by the rest.

Simple input restriction

Out of the box

As I mentioned and explained before, justifying their names the digit-related editors will simply ignore inappropriate values coming from both key strokes and value changes (read: pasting something in). As a matter of fact, some editors will go even further – the Numeric, Currency and Percent with set min value at/above zero will ignore the minus sign/dash even though they would normally allow it. Here’s an example setup:

  1. <p>Numeric Input:</p>
  2. <inputname="numeric"/>
  3. <br/>
  4. <p>Currency Input with set minimal value to 0: </p>
  5. <inputname="currency"/>

And the initialization scripts:

  1. $("input[name='numeric']").igNumericEditor();
  2.  
  3. $("input[name='currency']").igCurrencyEditor({
  4.     minValue: 0
  5. });

From which you get two jQuery editors that only allow number input along with the current culture’s decimal separator sign. However when you try to enter a negative number:

jQuery Editors automatically restricting entry based on type and settings (no minus for positive only values set).

In addition to that, those editors that would allow a dash(minus) will only allow it in front the actual number and will be allowed just once… so, yeah,  no phones in the field to enter money or quantity :)

Note: Also important is to mention that as expected from such controls when maximum and minimum values are defined they are enforced – you can think of this as another way input is restricted. Even when the user decides to enter an allowed, but unacceptable value, numeric-like editors will correct it to the minimum or maximum value. We’ll go deeper into that behavior in another article perhaps.

Conditionally preventing input

This has been asked by customers and I do believe it’s a valid point – in many cases editors need to be disabled for input until some condition is met – proper selection somewhere, valid input in another input in a chain entry situation. It’s really easy to implement – for the sake of the example we’ll use a checkbox to enable/disable to input and change the ‘disabled’ option available for all editors based on that:

  1. <inputid="toggleInput"/>
  2. <inputtype="checkbox"onchange="toggle(event)"checked="checked">
  1. function toggle(evt) {
  2.     $('#toggleInput').igNumericEditor("option", "disabled", !evt.target.checked);
  3. }

When disabled the inputs won’t accept additional input and have grayed-out style applied:

jQuery Editors input toggled betwen active and disabled.

Even though it might not be completely related, the editors also have ‘hide’ and ‘show’ methods – I suppose hiding an input when input in it is not to be allowed can be a kind of alternative to disabling.

Masks and hints

Of course, this is about the igMaskEditor and the help it can provide. The masks should feel fairly familiar to those that have previously used such functionality and easy enough to understand otherwise. They offer a rather wide variety as well as flexibility. With them you can sort of ‘mold’ the user input to a predefined template. They can be both obligatory (requiring input) and suggestive, the latter being of particular interest as they can very well serve as hints for the user. Besides actual input, the maks can also contain literals that can also provide helpful visual cues as to what is expected in that field. For example take the  very very standard telephone mask and lets see how we can solve the issue represented with the topmost image. Take a simple input:

  1. <inputid="phoneMask"/>

And turn it into a Mask editor – the related property accepts a special string that contains both special(reserved) characters and literals:

  1. $("#phoneMask").igMaskEditor({
  2.     inputMask: "(999) 999-9999"
  3. });

Set up like that the editor will behave exactly like a date editor (strictly digits only with visual cues), because the 9 stands for optional digit:

jQuery Mask Editors' mask serving as a template both restricting and hinting the user input.

I think it’s fair to say that this particular pattern is easily enough to recognize and serve as a guide for the user, while also applying restrictions.

But masks can do so much more than this! For example, take a name input field – you can capitalize the first letter for the user:

  1. $("#nameMask").igMaskEditor({
  2.     inputMask: ">L>L?????????????????"
  3. });

This way you can ensure the names will always start with a capital letter and when validation is used the name field won’t accept input less than two letters (‘L’ is the filter flag for a required letter, ‘?’ is optional letter). The greater than sign starts and stops capitalization and similarly the less than sign can make everything lower case.

Here’s a list of the rest of the filter flags accepted for a mask (everything else (or when escaped with "\\") is treated as literals, of course), in pairs (required/optional):

  • ’&’ / ‘C’: any keyboard character
  • ‘A’ / ’a’: letter or digit character
  • ’L’ / ‘?’: letter character
  • ’0’ / ’9’: digit character
  • #: digit character or "+" or "_"

The complete list is available in the jQuery Mask Editor’s API reference under the “inputMask” property.

Again keep in mind that input in mask editors is quite pretty much forced into the template and literals are static as far as the user in concerned and can serve as either part of the actual value or simply as visual cues, depending on the data mode.

Validation and regular expressions

I cannot deny that for the purposes of guiding users validation is invaluable. I won’t go into much detail about the specifics of the igValidator (the one responsible  for the feature in the editors), but I will note a few interesting things that can be helpful in leading end users to the expected input. Of course, the most basic is showing an simple error message when something is wrong. However those are mostly generic (the default ones I mean) and could be made more useful with just the slightest effort on your side. For example if you simple enable the validation for the mask editor show above you will get the following error message:

The default validation message for the jQuery mask editor.

That’s good and all, but unless you use “padChar” property to actually show the required fields this isn’t all that useful information. You can always override that message and provide the user with a description of what is expected or even provide additional examples, links and whatever you see fit really:

  1. $("#nameMask").igMaskEditor({
  2.     inputMask: ">L>L?????????????????",
  3.     validatorOptions: {
  4.         errorMessage: "Should be atleast two letters long. <br><em>Example: Jo </em>"
  5.     }
  6. });

Yes, simple HTML works too, content is not encoded, so the result from the above looks quite nice:

jQuery editors custom validation message

When you can’t fit the mold.. regular expressions!

As the fair warning above mentioned, masks are just a s restrictive as they are useful. I’ve reached to this conclusion when attempting to create an editor for the Northwind (AdventureWorks Sample Databases (MSDN)) Product object and more specifically it’s quantity per unit field. Believe me, when I say I tried masks of all kinds, but none would fit quite right, because the field would contain values like “48 - 6 oz jars” and then “10 ba” or “10 boxes x 12 pieces”. That is quite diverse and you might be wondering “How am I to validate this? How to let the user know when something is wrong?”.

The answer is quite simple – all editors, except the igMaskEditor, will work happily with regular expression to validate the input. This is just about as awesome as it gets, because you get to keep all other functionality as is, but achieve tremendously more complicated validation on the user input and allow for such irregular input as the above with the flexibility regular expressions can give you! Let’s solve the above problem and we can do that with a simple igTextEditor:

  1. <inputid="quantity"/>
  1. $("#quantity").igTextEditor({
  2.     validatorOptions: {
  3.         // need the  input to start with a number (quantity) and also contain e measure unit (word)
  4.         regExp: /\b[0-9]+ \b[A-z]/,
  5.         errorMessage: "Should contain a numerical value for quantity, followed by space and a measuring unit. <br><em>Example: 20 kg </em>"
  6.     }
  7. });

To explain a bit – the regular expression will match a ‘word’ starting with a number and containign number only (no limit on length), followed by a space (literal) and then another word of letters only exactly after it. That means it will match things like ‘4 g’ and the second part of ‘200 – 205 g packs’:

jQuery editor validation based on regular expression.

JavaScript's RegExp Object will provide you the power to create extremely complicated rules and this way you can provide enough freedom to your end users to keep them happy and still detect and inform them when they need to do better. You can even do basic input fields for email:

  1. @(Html.Infragistics().TextEditor()
  2.     .ValidatorOptions(validate =>
  3.         validate.RegExp("\\b[A-z0-9._]+@[A-z0-9.-]+\\.[A-z]{2,4}\\b"))
  4.     .Render())

You can expand this to be as flexible as required.. up to implementing the whole RFC specification if that’s your thing.

Demos and resources

  • You can fiddle with two demos of your choosing:

- A JSFiddle demo, where you can go ahead and test everything described above instantly.

- An ASP.NET MVC project showing both setting up in script and using the Helpers. Keep in mind the MVC helpers do require the Infragistics MVC assembly, see below on how to get yourself one.

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Summary

The jQuery Editors can truly make the editing and user input experience much better for the user, among other things. You have a plentiful toolset of editor types and settings that can control and  restrict and guide the end user to the desired input – editors accepting numeric values, just positive numbers. You can enable, disable or hide editors based on your own logic to achieve the editing results you want. The Mask editor offers a rich filter flag set to help you build a template for the users to follow and when that fails you can turn to complex regular expressions to know when the provided value is not valid and then also display a custom message to better explain the user what to do.

jQuery Editors: Rich client experience

$
0
0

jQuery editors for rich client experienceOnce you get to know Infragistics’ set of jQuery Editors you begin to realize they are packed with various options and all sorts of knobs to be tweaked. It’s quite the rich API and it can be put to good use to create awesome client-side experience for the end-user. You get elaborate options, events and methods that will help you create functionality to delight or you can better adjust the editors to your page with styling and to your users with localization. That’s a lot of defaults and with just a little effort you can go even beyond that. We’ll take a look at some of the valuable methods and what you can do with them plus nifty events tricks; some combinations of default options that truly transform the editor and a DIY localized currency calculator field and more.

In case you missed the previous two articles on those here’s a quick jump list:

Getting and Setting values

So far in the first article you’ve seen simple form submit in action, which while doubtfully is a way of getting values... well, it has not much to do with the actual widgets, more with the HTML input element. That is default bowser functionality assigning key-value pairs using the form input fields’ name attributes. That is also the reason why we’ve seen an extra input and additional extra ‘inputName’ property – the second hidden input the editors use to set the actual value while displaying it in some odd format. Except that, the client side controls have little to do with the way from data gets to the server. This is something that can be changed and it’s next on the menu!

All the editors offer the value method that is used to both get and set data. Note that when setting a value, the widget will parse for you to the appropriate type depending on the editor ( e.g. number for numeric) and should it fail it will be considered invalid input and rejected. On the other hand, when using the method to get a value, you should be aware what you get would be dependent on the data mode for some controls (e.g mask or date editor). Fortunately, their data mode defaults are set up to allow you not to worry about that.

The value method:

  1. $("#category").igNumericEditor("option", "value");

That would return whatever value is currently in the editor as number. Yes, for simple numeric editors you can snatch the value directly from the HTML input, however, it will come as string. However take a Mask field with its data mode set to 'rawTextWithRequiredPromptsAndLiterals' and you get something quite different:

jquery mask editor value with literals prompts

Same thing with igDateEditor and igDatePicker - they have a mask, of sorts, by default, so what you see is not what you get as the default value for those editors is a Date object, naturally. That leads to the following result from calling the value method:

jquery editors date value with separate date format in the input field

Again, you need to be aware of those and change the data mode if the defaults don’t fit your needs. Setting values adds the new one as third parameter for the method:

  1. $("#category").igNumericEditor("option", "value", product.CategoryID);

NOTE: Passing an inappropriate value will possibly cause it to fail the parsing and come out as null. Also, if you suddenly feel a little mischievous and go for the HTML input value and set there something unacceptable, note that the editor will still be keeping the actual value and will restore it if needed upon first interaction. Just so you know..

Form submit the AJAX way

Again in comparison to the previous usage of form submit – the fact that it causes a postback in itself can be a somewhat of a user experience hiccup, definitely is for me. To address this, using the value method we can send all the values back to the server in a simple AJAX request and enjoy better page behavior  - no page reloads and if you bother to check for actual changes, reduced requests. Something that really helps when dealing with multiple editors, like I’m sure you would in a edit form, you get a bunch of different editor widgets with different names, which severely hinders the ability to loop through and collect values.

There are two solutions to this really – one being, if suitable, to pick all the values from the HTML inputs instead as they can be selected and looped through. I used this, because the receiving action in the ASP.NET MVC application handles casting the values back to their intended. Speaking of MVC, it is the MVC wrappers that can help a lot in this – for the sake of unified helper output code they initialize all editors with  the base ‘igEditor’ class and setting types. But then again that is so very much not a bad idea – using the wrappers and just in script if you define all editors like that it would be the base class used for all method calls so you would  easily be able to loop through and get the values in proper types too! Since all jQuery widgets store their data using the .data() method they can be accessed the very same way – you do need the widget name, and that’s why using the base editor makes this so easy, here’s a super simple method (more elaborate version is available in the demos):

  1. function save() {
  2.     var values = [];
  3.     $('#details fieldset').find('input').not(':radio').not(':hidden').each(function () {
  4.         var editor = $(this).data('igEditor') || $(this).parent().data('igEditor');
  5.         values.push(editor.value());
  6.     });
  7.     //send back to serv
  8.     $.post('@Url.Action("Update")', values);
  9. }

Selecting just the right inputs completely depends on your own markup really - since this save function is used for editors created with the ASP.NET MVC helpers (they create a hidden input to store the value with the proper name for you)  I need to exclude those, as the actual widget is initialized on the visible input element. Also I have some radio-s that need to excluded as well. Then you also have something specific for the MVC helpers as well – editors with the spin buttons (they are SPAN elements) are wrapped in a parent span and the widget logically is initialized on it – thus the ‘.parent()’ call when the widget is not found on the input.

Being stubborn and all…

Once you get the widget reference you can access all options, properties and methods… validate as well! So yes, you are not in any way forced into form submitting to get proper validation going on. Also, to justify the title, in my script only demo part I have initialized each editor like I normally would with their extended versions. That, based on what I explained above, should make it rather impossible to loop through the widgets, or would it? The thing is you can still search for the widget reference, so what follows is a method that would search for all jQuery Editors in the selection and call validate on those found and return a bool if the group is valid (basically to be used to stop sending the values to the server, like a normal submit would):

  1. function validateAll() {
  2.     var areValid = true;
  3.     $('fieldset').find('input').not(':radio').each(function () {
  4.         var editorkey = Object.keys($(this).data()).filter(function(x){
  5.             return /\big[A-z]{0,8}Editor\b/.test(x);
  6.         })[0];
  7.         //trigger validate and if no false has been met assign the result
  8.         if (editorkey) {
  9.             areValid = $(this).data(editorkey).validate() && areValid ? true : false;
  10.         }
  11.     });
  12.     return areValid;
  13. }

Lines 4 through 6 would loop through all the property keys of the object returned by .data() and find the one that match the regular expression above (basically anything like ‘ig*Editor’ will pass) and take that key and use it to get the widget reference. The rest is simple call to validate and retaining false value if ever met, but the point is this way of getting the key can be reused for any functionality that seeks to find all the igEditor-s.

Invalid input behavior

One particular thing that I found different from other platforms’ editors is the lack of readily available invalid behavior (perhaps it can be added by popular demand, eh? what do you say?). Still the fact is that this can very well be achieved be handling the default editor events such as the ‘value changing’. Notice the ‘-ing’? Means this even can be canceled and if it is, well the actual change is as well! That means you can override the default set min/max value behavior by rejecting the input and leaving the old value instead!

  1. $("#category").igNumericEditor({
  2.     dataMode: 'int',
  3.     maxValue: 8,
  4.     minValue: 1
  5.     valueChanging: function (evt, ui) {
  6.         if(ui.value > ui.owner.options.maxValue){
  7.             //ui.owner.value(ui.oldValue);
  8.             returnfalse;
  9.         }
  10.     }
  11. });

Note that there are two ways to do that – cancel the change or change the value back. The latter can be used if you still want your ‘value changed’ to be fired.

Now with more… spinning!

jquery editors numeric spin buttons

One other favorite feature of mine  - the spin buttons. They are very easy to set up and provide quite the functionality out of the box. As you would expect for a numeric-like editor (Yes, yes, date editors included!) the spins with increment/decrement the value by one (the default for date is also one day). The spin buttons can help the end user quickly and sometimes more precisely get to the proper value. For that to happen, of course, the settings should match the usage scenario, but let’s say we want to go through a lot of values fast. The ‘spinDelta’ can be very helpful and the user can actually hold a spin button down and the speed as with value changes are applied gradually increases to a quite high rates!

Another extremely useful feature which you get out of the box – spin wrap around! What it does is when the user attempts to spin beyond the defined minimum and maximum values (normally that would do nothing), it allows that action to move to the other end of the value list – from max to min and the other way around, basically a rewind for the values. So lets improve on the editor code showed last by adding the spin buttons with all the enhanced functionality:

  1. $("#category").igNumericEditor({
  2.     dataMode: 'int',
  3.     maxValue: 8,
  4.     minValue: 1,
  5.     spinDelta: 3,
  6.     button: 'spin',
  7.     spinWrapAround: true,
  8.     valueChanging: function (evt, ui) {
  9.         if(ui.value > ui.owner.options.maxValue){
  10.             returnfalse;
  11.         }
  12.     }
  13. });

But wait, there’s more to these buttons:

jQuery text editor with spin buttons

Does that look awkward in any way? It looks like an editor with spin buttons but with text in the field?! Since this is a custom input implementation that does not use any part of the HTML input (of type number) the spins are not limited to just numbers! They can be instead used on even the simple text editor that has a list of items. Yes, that means you can provide a set of items and the user would be able to loop through them using the spin buttons. And wrap function works here(..and well you can change the button property to dropdown to get those in a list even, more on that in the future):

  1. @(Html.Infragistics().TextEditor().ListItems(newList<string> { "aspnet","jquery", "ios", "reporting", "wpf", "sharepoint"}).ListAutoComplete(true).ListMatchOnly(true)
  2. .ButtonType(TextEditorButtonType.Spin).SpinWrapAround(true).ValidatorOptions(validate => validate.OnChange(true).OnBlur(true))
  3. .Render())

Style

As you might have notice the editors have been sporting some clean Modern design (why do I always feel like saying ‘Metro’?). Even with as little screen real estate as these controls take, you can still tell it’s pretty – sharp edges and colors, nice effects (especially of the validation messages and oh my don’t even get me started on the date picker). Go see them on the Editors Windows UI Theme Sample! And you can also use them with custom styles!

TL;DR | Summary

Stay tuned for the second part and DEMOS! When I mentioned something about a rich API it was no joke – so far some of the things showed above might’ve been more appropriate for the ‘guiding the user’ blog, but hey they just won’t fit, and at the end of the day those jQuery Editors’ job is to make both the user happy and the data reaching the server good, so almost everything overlaps.

So far we’ve covered how to get and set values, how to send them to the server without causing a postback via AJAX. Also on the list was how to get widget references for multiple editors and possibly using this to get values and/or validate them. I’ve mentioned some ASP.NET MVC specific differences that should be kept in mind, like events. The latter you can use to exercise better control over the way editors handle invalid input. You can also delight users with spin button functionality that would also work with predefined text values and and top it all off with some looks with themes and custom styles.

And I’m leaving some pretty neat tricks with localization and validation for the second part of jQuery Editors experience!

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

jQuery Editors: Rich client experience (part 2)

$
0
0
 

jQuery editors rich client experienceTime for some more fiddling with the elaborate API of the jQuery Editors helping you deliver awesome experiences! Here’s the quick jump list for the previous articles:

Still spinning

Well there’s one more trick you can do with the spin buttons and the dropdown too – you can make the editor read only (no not disabled like in the first article). The trick with read-only editor is that, yes – the field itself will not accept input, but the buttons still work just fine! And as you’d imagine there’s a property to allow interaction with those to still change the value and I also find useful the spin event to make it crystal clear to the user what happened exactly:

  1. <label>Units in stock:</label>
  2. <inputid="stock"/>
  3. <labelstyle="height: 0; display: none;"id="stockInfo"></label>
  1. $("#stock").igNumericEditor({
  2.     button: 'spin',
  3.     spinDelta: 10,
  4.     readOnly: true,
  5.     spinOnReadOnly: true,
  6.     minValue: 0,
  7.     spin: function (evt, ui) {
  8.        $("#stockInfo").stop(true,true)
  9.        .text(ui.delta).css('color', ui.delta > 0 ? 'green' : 'red')
  10.        .fadeIn(200).delay(100).fadeOut(200);
  11.     }
  12. });

And the result of that is:

jQuery editor with spin buttons, delta and event handle

So, you can have an edit field and make sure without any need to check that the values can only go within a range and they can change by exactly as much as you want and in the meantime add a cool and helpful effect for the user (if you are changing the delta in runtime). Check out the demos – there you can find another field with spin buttons (showing the product category) and since that is just an ID and the actual category name is in a separate table, using events like this you can make calls to get the name of the category to display so the user can actually make sense of the numbers.

On a side note, to clarify the difference between this and disabled – when you disable all interactions are cut off, while a read-only input still can be given focus, interacted with and fire events and such.

A basic combo

I mentioned a few times so far you can create editors with dropdowns instead of spin buttons and they behave much like a basic combo box would with plenty of features. Let’s take the spin-button example from last article and use it with a dropdown. One neat trick is that when you provide the list of items it doesn't have to be strings – can be objects with ‘text’ property to be used instead. And if those objects happen to have a ‘getHtml(')’ method – the result of it will be the representation of the item in the combo. That means you can produce amazing template combos and still get the auto-complete functionality for text like you would with the spins:

  1. <label>Autocomplete field(pick a product):</label>
  2. <inputid="autoText"/>
  3. <scripttype="text/javascript">
  4. // the data object and an array of it:
  5. function textObj (txt){
  6.     this.text = txt;
  7.     this.getHtml = function(){
  8.         return"<p>" + this.text + "&nbsp  (<a href='http://www.infragistics.com/products/" + this.text + "' style=\"color: #000;\" target='_blank'>read more</a>) </p>";
  9.     };
  10. }
  11. var textData = [new textObj("aspnet"), new textObj("jquery"), new textObj("ios"), new textObj("reporting"), new textObj("wpf"), new textObj("sharepoint")];
  12.  
  13. $.ig.loader(function () {
  14.     $("#autoText").igTextEditor({
  15.         listItems: textData,
  16.         button: 'dropdown',
  17.         listMatchOnly: true,
  18.         validatorOptions: {
  19.             onblur: true,
  20.             onchange: true
  21.         }
  22.     });
  23. });
  24. </script>

The list of Infragistics products is obviously incomplete, it was random, sorry :\

jQuery text editor with dropdown!

Runtime localization

Except shipping you page/app with controls that are already set, the editors handle dynamic regional settings quite gracefully as well. To have access to the required localization resources you would need either the specific regional settings you’d want to use or form simplicity the whole bundle. For detailed information loop up the localization section in the Using JavaScript Resources in NetAdvantage for jQuery  and the Customizing the Localization of NetAdvantage for jQuery Controls one. For this simple case all that is needed is the resource file and I will just let the Loader figure when/where those should be placed. You can do that by adding a path to the file:

  1. $.ig.loader({
  2.     scriptPath: "http://cdn-na.infragistics.com/jquery/20121/2049/js",
  3.     cssPath: "http://cdn-na.infragistics.com/jquery/20121/2049/css",
  4.     resources: "igEditors,igValidator,modules/i18n/regional/infragistics.ui.regional-i18n.js,igCombo",
  5.     theme: 'metro'
  6. });

or use the intended regional property (and yes you can put the bundle ‘i18n’ file there too!):

  1. @(Html.Infragistics().Loader()
  2.     .CssPath("http://cdn-na.infragistics.com/jquery/20121/2049/css")
  3.     .ScriptPath("http://cdn-na.infragistics.com/jquery/20121/2049/js")
  4.     .Resources("igEditors,igValidator,igCombo")
  5.     .Regional("i18n")
  6.     .Theme("metro").Render())

Now you have all the local setting s available at any point and all editors can have regional settings and you can apply any at any point. With some event handling you can do that and achieve quite interesting results with some controls, like for example the Currency editor, which you can turn into a currency conversion field:

  1. <divid="details">
  2.     <label>Price in:</label>
  3.     <inputtype='radio'name="currency"value="en-US"checked/>
  4.     <label>USD</label>
  5.     <inputtype='radio'name="currency"value="bg"/>
  6.     <label>BGN</label>
  7.     <inputtype='radio'name="currency"value="en-GB"/>
  8.     <label>GBP</label>
  9. </div>
  10. <label>Unit price:</label>
  11.      <inputid="price"/>
  12. <scripttype="text/javascript">
  13. var USDto1GBP = 1.6190, BGNto1USD = 1.51;
  14.  
  15. $.ig.loader(function () {
  16.     $("#price").igCurrencyEditor({
  17.         maxValue: 10000,
  18.         minValue: 1,
  19.         regional: "en-US",
  20.         valueChanged: function (evt, ui) {
  21.             //store base (USD) value in the element
  22.             var regional = $('input:checked').val();
  23.             switch (regional) {
  24.                 case"en-US":
  25.                     $("#price").data('value', ui.value);
  26.                     break;
  27.                 case"bg":
  28.                     $("#price").data('value', ui.value / BGNto1USD);
  29.                     break;
  30.                 case"en-GB":
  31.                     $("#price").data('value', ui.value * USDto1GBP);
  32.                     break;
  33.             }
  34.         }
  35.     });
  36. });
  37.  
  38. //event handler for all 3 radio-s
  39. $('#details :radio').change(function myfunction(evt) {
  40.     var index = $('#details :radio').index(this);
  41.     switch (index) {
  42.     case 0:
  43.        $("#price").igCurrencyEditor("option", "value", $("#price").data('value'));
  44.         break;
  45.     case 1:
  46.         $("#price").igCurrencyEditor("option", "value", $("#price").data('value')*BGNto1USD);
  47.         break;
  48.     case 2:
  49.         $("#price").igCurrencyEditor("option", "value", $("#price").data('value')/USDto1GBP);
  50.         break;
  51.     }
  52.     //apply regional
  53.     $("#price").igCurrencyEditor('option', 'regional', this.value);
  54. });
  55. </script>

This is a-a-almost the full snippet needed to get this to work (just missing the loader from previous snippet :) ). Of course, as you can see the exchange rates are static (hey, they are up to date as of the moment of writing at least), but you can easily pull those from some currency exchange API out there. In this scenario values are actually fed to the editor in USD and that is the base value that is always retained – the rest are calculated from it and on change back to it when different regional is applied:

jQuery currency editor with dynamic regional settings

Make sure you check out one the demos and give this one a try yourself.

Enhancing the validation

Hiding the validation error message

I find this to be particularly useful, because error messages tend to stick around until valid value is entered, even when you don’t want to fix it right now. That’s especially annoying when you have other fields around stacked quite tightly (like I did) and that error message just gets in the way of something else. Not to worry! I have a small snippet to solve that globlally – hooking up to the igValidator event fired when the message is shown and setting a timer for it to be hidden!

  1. //hide error message cuz its evil >:D
  2. $(document).on("igvalidatorerrorshown", function (evt, ui) {
  3.     setTimeout(function () {
  4.         //but keep the error'd css
  5.         ui.owner.hide(false);
  6.     }, 2500);
  7. });

The best part is since each editor initializes its own validator instance those timers don’t interfere with each other and since passing false makes the hide skip removing the specific CSS the editor remains with red borders to remind you that it’s still something wrong. A possible improvement on this would be to handle the focus event and show the message back then, not after another validation pass.

Making use of events

I have this quite lovely example (in my eyes at least, so bear with me) of a Mask editor functioning somewhat like a Hangman game, well, for the limited attempts at the very least. While this doesn’t entitrely have to be a copy-past kind of a deal, like you could use the message hiding, I do believe a more adaptive validation – being able to react to a certain number of bad attempts without the server participation is actually quite  - might be quite nice. This is to showcase the capabilities the events and their arguments give you. Here’s what we have:

  1. <label>Hangman (hint: IG):</label>
  2. <inputid="mask2"/>
  3. <labelid="value"></label>
  4. <scripttype="text/javascript">
  5. $.ig.loader(function () {
  6.     $("#mask2").igMaskEditor({
  7.         
  8.         inputMask: ">ILLLLLILLILL",
  9.         dataMode: 'rawTextWithRequiredPromptsAndLiterals',
  10.         emptyChar: "_",
  11.         valueChanged: function (evnt, ui) {
  12.             $("#value").text("Value: " + $("#mask2").data("igMaskEditor").value());
  13.         },
  14.         validatorOptions : {
  15.             attempts: 4,
  16.             onblur: true,
  17.             onchange: false,
  18.             checkValue: function (evt, ui) {
  19.                 if (ui.value !== "INFRAGISTICS") {
  20.                     if (!ui.owner.options.attempts) {
  21.                          ui.message = "No attempts left";
  22.                          $(ui.owner.element).igMaskEditor("option", "disabled", true);
  23.                          returnfalse;
  24.                     }
  25.                     ui.message = "Try again. Attempts left: " + (--ui.owner.options.attempts + 1);
  26.                     returnfalse;
  27.                 }
  28.             }
  29.         }
  30.     });
  31. });
  32. </script>

using events to control and adapt the validation

Since when providing options for the validator those are added to the widget itself, you can in essence add your own, like I did here and use it as an internal counter, rather than keeping track of that in external variable(s). Also notice that this is the ‘check-value’ event, not the validation one. This happens before the value is evaluated, and well, you can do that yourself and if not to your liking, even though it would otherwise satisfy the rules, returning false will cause the validator to consider that invalid.

This method can be very useful to recreate real world validation purely on the client – limited number of attempts and when the user fails too many times you redirect him to a different page or display additional verification fields (The ‘prove you are human’ Twitter test, for example, load a Captcha..).

MVC  Gotcha-s

So besides the tricky part with all editors being of the base class type with the ASP.NET MVC helper and having the markup generated for you (which is sometimes different than you might assume), there’s some more quirks.

As you’d imagine you can’t go out of your way and add random attributes to the validator options with the helpers like it’s done above. You can still do it in simple script tag one the widget has been initialized. Also just for now you can’t really pass the ‘item lists’ array of objects as it kinda expects a List<string>.

One other notable gotcha – when dealing with regular expressions (and I might have totally missed that one before). By default the relevant validator property accepts both a string or the ready Regexp object. However MVC helpers, being helpful as they are, write strings to the document, therefore eating your escapes. And the parsing of the string to RegExp calls to string and it eats up another escape..so to speak. So if you are using sequences that need to be escaped once in a normal regular expression like ‘\b’ those must be escaped three times in the wrapper:

  1. @(Html.Infragistics().TextEditor().ID("qpu").Required(true)
  2. .ValidatorOptions(validate =>
  3.             validate.CustomErrorMessage("Should contain a numerical value for quantity, followed by space and a measuring unit. <br><em>Example: 20 kg </em>")
  4.         .RegExp("\\\\b[0-9]+ \\\\b[A-z]").OnBlur(true).OnChange(true))
  5. .Render())

Basically this reaches the client as ‘\\b’ and after another call of toString() and up as the proper ‘\b’ to be converted to expression.

Demos and resources

  • You can fiddle with two demos of your choosing:

- A JSFiddle demo, where the submit button is only good for triggering validation.

- An ASP.NET MVC project showing both setting up in script and using the Helpers. Keep in mind the MVC helpers do require the Infragistics MVC assembly, see below on how to get yourself one.

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn! Stay tuned some pretty exciting stuff are happening for the next release!

TL;DR | Summary

Don’t say I didn’t warn about the jQuery Editors API – the fact is it’s quite a lot, it has to provide support for several different controls after all! As in the latest series you’ve seen how much you can tweak and drastically change the experience – rich client-side editing with no postbacks, flexible validation that you can even mix with your additional logic to create adaptive behavior, or make a behavior for invalid values. Even the little oddities of the ASP.NET helpers are no problem once you get to know them and perhaps we’ll continue soon with some more MVC editor fun..!

How to get started with the XAML Syntax Editor

$
0
0

XAML Syntax Editor - How to get startedThe XamSyntaxEditor is one of the recent additions to the Infragistics toolset. It made an odd voyage from being announced as a CTP not  with a release, but with a service release a bit over two months ago. And now it is on route to a XAML project near you, which was announced Brian Lagunas in What’s New in NetAdvantage for WPF & Silverlight 12.2: Part 1 – XAML Line of Business Controls.

A little bit on the side note – check out all the cool LoB stuff coming soon and make sure you don’t miss the What’s New in NetAdvantage for WPF & Silverlight 12.2: Part 2 – XAML Data Visualization Controls!

Now that you’ve got your basics lets jump into more details and examples.

 

Quick setup

First things first – you will need the references to the required assemblies and either the latest service release of either NetAdvantage for Silverlight or WPF. If you are lazy like me just take advantage of the new search bar in Visual Studio 2012 (search field in every place imaginable, yes! yes! Thank you Microsoft!):

XAML Syntax Editor in the Visual Studio 2012 Toolbox

Now in case you don’t feel like using a toolbox – the list includes the base platform assembly, “*.Undo.v12.2”, “*.Documents.TextDocument.v12.2” and “*.Controls.Editors.XamSyntaxEditor.v12.2”.

At this point you can setup the Syntax editor in both XAML and in code. (Note: Since the editor won’t do much without a document I consider them a unity and therefore setting up the editor also includes adding in the document). In the absolute most basic case your editor would look like so in XAML:

  1. xmlns:ig="http://schemas.infragistics.com/xaml"
  1. <ig:XamSyntaxEditor x:Name="editor">
  2.     <ig:XamSyntaxEditor.Document>
  3.         <ig:TextDocument/>
  4.     </ig:XamSyntaxEditor.Document>
  5. </ig:XamSyntaxEditor>

And in C#:

  1. using Infragistics.Controls.Editors;

  1. XamSyntaxEditor editor = newXamSyntaxEditor();
  2. editor.Document = new Infragistics.Documents.TextDocument();

Important thing to keep in mind – the Text Document is responsible for the desired language interaction(highlighting and error validation) and it’s default setting is ‘Plain Text’. That as you can imagine does not support either of the previously mentioned two very important features, so to get them you might want to set a language using the language property:

  1. editor.Document.Language = new Infragistics.Documents.CSharpLanguage();

Out of the box the editor also supports VB.NET. Of course you can set the very same property identically in XAML and run the app and you’ll get:

XAML Syntax Editor with highlithing and error reporting for C#

*Random C# code pasted form the code behind not included.

The last line is where I deleted a semicolon and a closing parenthesis so now you the familiar squiggly underlines in red are there to indicate something is indeed wrong. And language key words are colorized in nice blue as expected.

What’s in the box?

Grammar

Back in the "What’s new” post – did you notice Syntax Parsing Engine? If you did then +1 for you! Yup, the lexer is actually an autonomous engine that is part of the core experience the Editor control provides. It is responsible for the parsing, creating of syntax tree and error reporting. As mentioned it does come with support for  plain text, C# and VB.NET, but most importantly EBNF(Extended Backus–Naur Form) so you can add additional language descriptions, but on that some other time.

Clipboard support and Undo/redo manager

Also by default the Syntax editor comes with clipboard support for all common actions and also support for unlimited undo/redo operations. Does that sound familiar? Remember the not so long ago introduced Undo / Redo Framework for XAML applications? Yup – it is right here with all it’s features – the unlimited operations stack of Undo Units, flexibility, etc. You can set up properties of the manager from the property of the Text Document:

  1. <ig:XamSyntaxEditor x:Name="editor">
  2.     <ig:XamSyntaxEditor.Document>
  3.         <ig:TextDocument>
  4.             <ig:TextDocument.UndoManager>
  5.                 <ig:UndoManager UndoLimit="20"/>
  6.             </ig:TextDocument.UndoManager>
  7.             <ig:TextDocument.Language>
  8.                 <ig:CSharpLanguage></ig:CSharpLanguage>
  9.             </ig:TextDocument.Language>
  10.         </ig:TextDocument>
  11.     </ig:XamSyntaxEditor.Document>
  12. </ig:XamSyntaxEditor>

Text Documents , Snapshots and Scanners

You might actually see this as the TextDocument framework, where the document represents a single text file and supports languages and operations like Load and Save, Append, Delete, Find and Replace and Insert and quite a lot more! It that you have (among other ways) a snapshot and the TextDocumentSnaphot represents the entire state (i.e. version) of a document. Every time a change is made to the document a new snapshot is created. Each snapshot is thread-safe and immutable. The snapshot provides access to lines, length, search method and a one to create a scanner. TextDocumentSnapshotScanner class targets a specific instance of a TextDocumentSnaphot and is created via the snapshot’s CreateScanner method. It provides neat methods to navigate between lines, words and tokens.

 

As you can tell the XamSyntaxEditor is quite the composite control but is it also very flexible and extensible. It strives to provide Editing experience much like the one you have in Visual Studio and just like it implements APIs that are familiar to Visual Studio extension writers.

Splitting and visual cues

If you have noticed above in the screenshot this is the Editor in Metro/Modern/Win8 style (seriously, got all confused lately!). Even though it doesn’t really have that much UI, there are still scroll bars and.. two odd rectangles. What I can’t show you on a screenshot is the mouse cue you get when on top of it as it prompts for resizing. That is because those are handles to drag and actually split the view!

Split handles, Verical and Horizontal split in the XAML Syntax Editor

*Sorry for the slightly large gif and slightly buggy recording :)

Both views are scrollable and maintain their own selection. The editor also has property to allow some or none of the splitting options to the end-user.

Commanding

The XamSyntaxEditor comes with a substantial set of commands! I mean there’s a list and it’s kind of long. From default things like Cut/Copy and Paste to split or moving the view in various directions to Undo manager commands and even to upper or lower case and selection. It’s a rich choice and I’ll just show you a small example of a two ButtonTools (consider them to be normal buttons if you will) on a XamRibbon:

  1. <igWPF:ButtonTool Content="Undo" SmallImage="/WpfApplication1;component/Images/undoArrow.png" IsQatCommonTool="True" >
  2.     <ig:Commanding.Command>
  3.         <igPrim:SyntaxEditorCommandSource EventName="Click" CommandType="Undo" />
  4.     </ig:Commanding.Command>
  5. </igWPF:ButtonTool>
  6. <igWPF:ButtonTool Content="Redo" SmallImage="/WpfApplication1;component/Images/redoArrow.png" IsQatCommonTool="True">
  7.     <ig:Commanding.Command>
  8.         <igPrim:SyntaxEditorCommandSource EventName="Click" CommandType="Redo" />
  9.     </ig:Commanding.Command>
  10. </igWPF:ButtonTool>

The syntax commanding source above contains all the command types I mentioned and probably some more.

Loading and Saving

The Syntax Editor’s Document is the one with the methods to load and save and both methods provide overloads to use either a string with the file name or a stream (the save method actually offers a few variations that can instead save using information from a previous call to the load method. I’ll throw a two fast examples of how to save locally but load from a remote file:

  1. ///<summary>
  2. /// Clear the document text (if any) and load some remote C# code file
  3. ///</summary>
  4. privatevoid LoadDocument()
  5. {
  6.     //if (this.editor.Document.CurrentSnapshot.GetLines().Count() > 0)
  7.     //{
  8.     //    this.editor.Document.InitializeText(string.Empty);
  9.     //}
  10.     WebRequest wr = WebRequest.Create("https://dl.dropbox.com/u/76144077/somecode.cs");
  11.     HttpWebResponse resp = (HttpWebResponse)wr.GetResponse();
  12.     try
  13.     {
  14.         this.editor.Document.Load(resp.GetResponseStream());
  15.     }
  16.     catch (WebException ex)
  17.     {
  18.         MessageBox.Show(ex.Message);
  19.     }
  20. }
  21.  
  22. ///<summary>
  23. /// Save file locally.
  24. ///</summary>
  25. privatevoid Save(object sender, RoutedEventArgs e)
  26. {
  27.     Stream stream;
  28.     System.Windows.Forms.SaveFileDialog dlg = new System.Windows.Forms.SaveFileDialog();
  29.     if (dlg.ShowDialog() == System.Windows.Forms.DialogResult.OK)
  30.     {
  31.         if ((stream = dlg.OpenFile()) != null)
  32.         {
  33.             this.editor.Document.Save(stream);
  34.             stream.Close();
  35.         }
  36.     }
  37. }

More!

There’s really so much more you can do as I mentioned the control is designed to be extensible, so once you gain substantial knowledge of the control you can customize it quite a bit. For now I’ll tease you with my pretty app where I have the above file loaded, the save, the commands in a ribbon , along with a search fields and a status bar with info bound to the Caret position for example:

  1. <TextBlock Text="{Binding Caret.TextLocation.Line, ElementName=editor, Mode=OneWay}"

And it looks and functions wonderfully:

XAML Syntax Editor application with clipboard, undo/redo, search, find and replace and status bar.

Stay tuned

This has obviously barely scratched the surface but it’s enough to get you started, provide basic info for the XAML Syntax Editor and show you some of the interesting commands, load and save methods and other properties.

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn! Stay tuned some pretty exciting stuff are happening for the next release!

Customizing the XAML Syntax Editor

$
0
0

Customizing the XAML Syntax Editor.The XAML Syntax Editor is pretty awesome control – I mean being able to create your own code editor is one and then just customize language and coloring and… add the fact that you can split the view and be on at line 50 and 500 at the same time and making changes in both spots! As you can tell, there are still plenty of neat things about the XamSyntaxEditor that I’d like to show you. I  showed you last time how to get started with this cross-platform control and with some basics covered lets dive into the options to customize and enhance the functionality and user experience of the control.

First to mention that even though the v2 release for 2012 is not yet out, you can still go grab the CTP version – download links can be found in Brian’s announcing post. Besides major spoilers on what features are to expected in the future, in the downloads you will find samples! But most importantly you can check out the XamSyntaxEditorStudio demo that features something similar to what I’d like to show you today and some other cool tricks:

XAML Syntax Editor Studion demo.

Even by looking at it by adding Find and Error panes it does feel Visual Studio-ish and that’s the goal.

Custom Margins

I’ll just mention this somewhat briefly to acknowledge it is there. The feature is actually the one providing for the line numbering and behave in much the same way – fixed/frozen/static/pinned area (man, I’ve seem a few variations of having something just not scroll along). And when you go for a custom margin you define the content and position –  top, left, right and bottom. The Syntax Editor allows for a large number of margins, but my guess is that, yes, you can cover your entire code window in margins but what good would that do? Personal advice – don’t go wild on the margins, one or two should be enough. Examples of how this works you can find in the samples I mentioned above and it kind of looks like this (squished):

Custom Margins in the XAML Syntax Editor

You will find this pretty well described in the upcoming documentation as well.

Colors and Typefaces!

Naturally as you’d expect form an Editor you can control a whole bunch of visual properties… and then some. For one you can customize the line numbering margin text with properties exposed directly in the editor and those include font family, style, weight and color. On a side note selection highlighting colors are also exposed as properties for you to tweak. Now, let’s assume I’d like me some Segoe UI numbers to match the system and I wouldn’t mind to have those in just slightly darker blue. But now my numbers are Segoe and the code is Consolas (Visual Studio’s default) and I’d like those to match. Even though by default the font is set to Segoe UI for me it didn’t take that seriously until explicitly defined:

  1. <ig:XamSyntaxEditor x:Name="editor" LineNumberMarginForeground="#FF089AEE" LineNumberMarginFontFamily="Segoe UI" FontFamily="Segoe UI" LineNumberMarginBackground="#FFEEEEEE" Background="#FFFDFDFD"/>

You can set different font, colors and backgrounds in the XAML Syntax Editor.

I gave the margin some gray background to match the ribbon and to be more clearly separated from the main view. As you can tell with font and background changes you can achieve that ‘dark theme’ look you can see in the XamSyntaxEditorStudio and I see on so many screens with VS.

Better yet..

There are other colors the editor does well – the syntax highlights! And you can very easily tap into those as well. And since there are quite a few language elements I’ll just mention you can tweak most. The control accepts a ClassificationAppearanceMap object to keep track of those modifications:

  1. ClassificationAppearanceMap map = newClassificationAppearanceMap();
  2. map.AddMapEntry(ClassificationType.Keyword, newTextDocumentAppearance()
  3.         {
  4.             FontSize = 13, FontFamily = newFontFamily("Segoe UI"), Foreground = newSolidColorBrush(Color.FromArgb(255,42,116,162))
  5.         });
  6. this.editor.ClassificationAppearanceMap = map;

As you can see you can add to the map by specifying a type key which can be a language keyword, but can also be comment, identifier, operator, number and so on. Even the SyntaxError is here so you can change how that looks too. In my example above I just made keywords a bit larger and in darker steel-blue tone.. it doesn’t look all too amazing so no screenie.

All those layers

The Editor displays content in four (five?) dedicated layers. You have the background as bottom layer and the caret(yup it has its own) as topmost. In-between there’s a text layer, one for selection and one for syntax errors. And then you can add you own implementing the ISupportPositioning interface and render content on any.

XAML Syntax Editor Document presentation layers.

Now you could ask what kind of content would you be rendering? Answer is hiding below somewhere..

Search, Find and Destr… Replace!

So here’s what I did – I went on and added two inputs to enter search criteria and replace text with the following method:

  1. privatevoid Replace(object sender, RoutedEventArgs e)
  2. {
  3.     var srchField = this.xamRibbon.GetToolById("srchText") asTextEditorTool;
  4.     if (srchField.Value != null)
  5.     {
  6.         var count = this.editor.Document.FindReplaceAll(newTextSearchCriteria(srchField.Text), this.replaceText.Text).Results.Count;
  7.         System.Windows.Forms.MessageBox.Show(count + " occurance(s) replaced.");
  8.     }
  9. }

Sure it works great and it even does the same as VS showing the message with how many words you just lost :)

But at this point I want to have a simple search and feeding empty string or even null instead replacement text are no options(one deletes every match and the other plain errors out). And then the Document has no simple search? Of course it does! The ‘Find All’ is actually in the current document snapshot. And since you get a collection of results, why not do something useful with it? The sample above goes to display the results in a box and move the caret to any of those. I’d like to iterate on that and mimic most editors’ behavior by providing navigation between results and ended up with this search utility or manager if you will:

  1. publicclassSearchUtility
  2. {
  3.     publicList<TextSearchResult> Results { get; privateset; }
  4.     publicXamSyntaxEditor Editor { get; privateset; }
  5.     publiceventEventHandler<EventArgs> OnSearch;
  6.  
  7.     privateint _location = -1;
  8.     
  9.     public SearchUtility(XamSyntaxEditor editor)
  10.     {
  11.         this.Editor = editor;
  12.     }
  13.  
  14.     publicvoid DoSearch(string criteria)
  15.     {
  16.         Results = Editor.Document.CurrentSnapshot.FindAll(newTextSearchCriteria(criteria)).Results.ToList();
  17.         if (Results.Count > 0)
  18.         {
  19.             _location = 0;
  20.             Editor.ActiveDocumentView.GoToTextLocation(Results[0].SpanFound.StartLocation);
  21.             Editor.ActiveDocumentView.SelectionManager.SelectNextWord();
  22.             //raise event:
  23.             EventHandler<EventArgs> handler = OnSearch;
  24.             if (handler != null)
  25.             {
  26.                 handler(this, newEventArgs());
  27.             }
  28.         }
  29.         else
  30.         {
  31.             System.Windows.Forms.MessageBox.Show("No results found for '" + criteria + "'.");
  32.         }
  33.  
  34.     }
  35.  
  36.     publicvoid ClearSearch()
  37.     {
  38.         if (Results.Count > 0)
  39.         {
  40.             Results.Clear();
  41.             _location = -1;
  42.             //raise event:
  43.             EventHandler<EventArgs> handler = OnSearch;
  44.             if (handler != null)
  45.             {
  46.                 handler(this, newEventArgs());
  47.             }
  48.         }
  49.     }
  50.  
  51.     publicvoid Next()
  52.     {
  53.         if (_location >= 0 && _location < Results.Count - 1)
  54.         {
  55.             Editor.ActiveDocumentView.GoToTextLocation(Results[++_location].SpanFound.StartLocation);
  56.             Editor.ActiveDocumentView.SelectionManager.SelectNextWord();
  57.         }
  58.     }
  59.  
  60.     publicvoid Previous()
  61.     {
  62.         if (_location > 0 && _location < Results.Count)
  63.         {
  64.             Editor.ActiveDocumentView.GoToTextLocation(Results[--_location].SpanFound.StartLocation);
  65.             Editor.ActiveDocumentView.SelectionManager.SelectNextWord();
  66.         }
  67.     }
  68. }

As you can see there’s even an event (that is connected with the next enhancement) and methods to perform search, navigate between the results back and forth and clear them. So with a simple input and two buttons this will achieve the following:

XAML Syntax Editor Find - Search with navigation

Note: By default Case Sensitive and Match whole word options are off. Also I think it’s fairly clear I’m selecting the next word rather than the exact match. I think it makes sense with some more visual from below…plus it makes my life easier as the Selection Manager provides a whole bunch of methods to select up and down, till end of line or even end of text and of course a single character, so the word is a quick and dirty solution.

Custom Adornments

There’s still something missing I feel, still not enough visual oomph . I like it when some editors highlight every match and this is exactly what I want and thanks to all those layers I can have highlights. Adornments are just the tool to get my content on those layers and guess what – selection itself is one and so are the syntax error lines. The editor comes with base generator and provider classes to extend. Providers are registered with the control and their method called when a new generator is needed (basically for every new document view and yes, works out of the box with split views). The latter are created to provide adorners for particular layer defined at initialization. If you notice below while calling the base constructor the background layer is picked (we don’t want those highlights to mess with anything else):

  1. ///<summary>
  2.  /// A Adornment Provider to be registered with the Syntax Editor.
  3.  ///</summary>
  4.  publicclassSearchHighlightAdornmentProvider : Infragistics.Controls.Editors.AdornmentGeneratorProvider
  5.  {
  6.      publicoverrideAdornmentGeneratorBase CreateAdornmentGenerator(DocumentViewBase documentView)
  7.      {
  8.          returnnewSearchHighlightAdornmentBase(documentView);
  9.      }
  10.  }
  11.  
  12.  ///<summary>
  13.  /// The actual generator. We'll be drawing highlights in the TextBackgroiund layer as can be seen in the ctor.
  14.  ///</summary>
  15.  publicclassSearchHighlightAdornmentBase : AdornmentGeneratorBase
  16.  {
  17.      AdornmentInfo _searchAdornmentInfo;
  18.      readonlyPath _path;
  19.      readonlyEditorDocumentView _documentView;
  20.  
  21.      public SearchHighlightAdornmentBase(DocumentViewBase documentView)
  22.          : base(documentView, AdornmentLayerInfo.TextBackground)
  23.      {
  24.          _documentView = documentView asEditorDocumentView;
  25.          _path = newPath { Fill = newSolidColorBrush(Color.FromArgb(180, 255, 222, 173)) };
  26.          _searchAdornmentInfo = this.AdornmentLayer.AddAdornment(this._path, newPoint(0, 0), null);
  27.          //subscribe to events that would require redraw
  28.          _documentView.LayoutChanged += UpdateHighlights;
  29.          MainWindow.SearchUtil.OnSearch += UpdateHighlights;
  30.      }
  31.  
  32.      ///<summary>
  33.      /// Only call draw when there are actual results. This can see a lot of improvements -
  34.      /// the LayoutChanged args provide collections with deleted, new/reformatted and translated lines.
  35.      /// Judging by that it is also possible to attempt to not redraw still good highlights.
  36.      ///</summary>
  37.      void UpdateHighlights(object sender, EventArgs e)
  38.      {
  39.          if (MainWindow.SearchUtil.Results != null&& MainWindow.SearchUtil.Results.Count > 0)
  40.          {
  41.              HighlightGeometry();
  42.              _path.Visibility = Visibility.Visible;
  43.          }
  44.          else
  45.          {
  46.              _path.Visibility = Visibility.Collapsed;
  47.          }
  48.      }
  49.  
  50.      ///<summary>
  51.      /// Get the line from the document view and create a rectangle with the measured size and position.
  52.      /// Do so for every match and then set them to the path.
  53.      ///</summary>
  54.      void HighlightGeometry()
  55.      {
  56.          GeometryGroup geometry = newGeometryGroup();
  57.          foreach (TextSearchResult res inMainWindow.SearchUtil.Results)
  58.          {
  59.              TextLocation start = res.SpanFound.StartLocation;
  60.              TextLocation end = res.SpanFound.EndLocation;
  61.              int lineIndex = res.SpanFound.StartLocation.Line;
  62.              if (_documentView.GetIsLineInView(lineIndex, false))
  63.              {
  64.                  DocumentViewLine line = _documentView.ViewLineFromLineIndex(lineIndex);
  65.                  Point startPoint = line.PointFromCharacterIndex(start.Character);
  66.                  Point endPoint = line.PointFromCharacterIndex(end.Character);
  67.                  geometry.Children.Add(newRectangleGeometry { Rect = newRect(startPoint, newPoint(endPoint.X, endPoint.Y + line.Bounds.Height)) });
  68.              }
  69.          }
  70.          _path.Data = geometry;
  71.      }
  72.  
  73.      ///<summary>
  74.      /// Called when settings in the <see cref="T:Infragistics.Controls.Editors.XamSyntaxEditor"/> (that could affect the display of adornments) have changed.
  75.      ///</summary>
  76.      protectedoverridevoid OnRefreshAdornments()
  77.      {
  78.          UpdateHighlights(null, null);
  79.      }
  80.  
  81.      ///<summary>
  82.      /// Called when the generator is unloaded.
  83.      ///</summary>
  84.      protectedoverridevoid OnUnloaded()
  85.      {
  86.          _documentView.LayoutChanged -= UpdateHighlights;
  87.          MainWindow.SearchUtil.OnSearch -= UpdateHighlights;
  88.          bool removed = this.AdornmentLayer.RemoveAdornment(this._searchAdornmentInfo);
  89.      }
  90.  }

Search results are still stored in the Search utility and the event is used to trigger the highlighting on search rather than just the default layout changed with the document view. The base generator provides the respective layer to which you can add your adornment in the form of any UI element really. Overriding the OnRefresh and OnUnload is somewhat optional, but recommended. The OnRefresh is called from the editor in case some external property that can affect adornment is changed that doesn’t necessary fire the layout changed. And the unload occurs either on unregister or every time the document loads now content (which in my case happens as I initialize with nothing and then load remote text). The base methods won’t do the cleaning up for you. Speaking of unregister, here’s how you register first:

  1. this.editor.Document.Language.ServicesManager.RegisterService("highlight", newSearchHighlightAdornmentProvider());

XAML Syntax Editor Search Highliting along with navigation

With this you get experience that is just like when you add ReSharper to Visual Studio and your searches now highlight all matches and you can navigate them! Well, in all fairness, my yellow is a bit off and I select a whole word no matter what, but those are two tiny details that can easily be tweaked to your liking :)

TL;DR | Summary

I’d like to end like I did the last time and that is by stating there is more and more to the control that I can manage to display. We took a brief look on how can margins be added to the control and tweaking various text elements. We also enhanced the search functionality by adding selection and neat navigation and topped that off with awesome highlights with custom adornment. I’m quite happy how they turned out and just as a tip you can use this approach to add any kind of visuals to your editor, just don’t go crazy on the drawing as it could get intensive.

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn! Stay tuned the demo and some pretty exciting stuff are happening for the next release!

How to get started with jQuery Grid Unbound Columns

$
0
0

Ignite UI jQuery Grid's Unbound ColumnsAn all new functionality with the second release for this year comes to bring more flexibility to your grid and the way it handles data. The unbound column is one of the classic grid features throughout Infragistics experience with grids and now user demands are met by the all-new-yet-same Ignite UI Grid :)

The feature itself allows for columns that can be used to render separate arbitrary list of items or calculated values usually based on other columns’ values. The columns support and work with all grid features – proper sorting, filtering, etc. Also normal column enhancements like column templates, formatters and any other column property are available. It is very simple in concept and then once you get a closer look and take into account how this would interact with features it can get quite complicated, but that would be something to dive into later on.

Why Unbound columns?

What good are they for? For one, imagine a situation I end up in quite often – I like using Entity Framework but when I reach to the part where data needs to be send to the client all hell breaks loose. Why on earth has this not been addressed is beyond me but the Entity objects are damn near impossible for the default serializers and this has been around for so long. So what you do is either go for third party ones or use Data Transfer objects that are meant for such cases and that works good for me most of the time and it reduces traffic too. In those cases when one data model is not enough and out of you control, for when you need data from different sources and the Mashup Data Source is too much hassle or you don’t even have some parts available when the grid is created… this is when an unbound column can totally save the day.

Then again that is not all – besides integrating two pieces of data, the unbound columns can be used to display completely new data. You can use the formula property to calculate values based on other values in the row. That means you can now create Summaries on per row basis, rather than the out-of-the-box ones for columns!

Quick setup

At first glance the unbound columns are no harder to define that any other -  as mentioned, all properties are available and features behave as expected. The single most important difference is the ‘unbound’ property that you can set to true in order for that particular column to be omitted when binding to the data source. Essentially you can get an empty column with no relation to the data model:

  1. columns : [{
  2.         unbound : true,
  3.         key : 'empty',
  4.         headerText : 'Empty',
  5.         dataType : 'number'
  6.     }
  7. ],

To put things in perspective, here’s the full grid definition.

  1. $('#grid1').igGrid({
  2.     width: '650px',
  3.     autoGenerateColumns : false,
  4.     autoGenerateLayouts : false,        
  5.     columns : [{
  6.             key : 'BusinessEntityID',
  7.             dataType : 'number',
  8.             headerText : 'B.E. Id'
  9.         }, {
  10.             key : 'SickLeaveHours',
  11.             dataType : 'number',
  12.             headerText : 'Sick Leave Hours'
  13.         }, {
  14.             key : 'VacationHours',
  15.             dataType : 'number',
  16.             headerText : 'Vacation Hours'
  17.         }, {
  18.             unbound : true,
  19.             key : 'empty',
  20.             headerText : 'Empty',
  21.             dataType : 'number'
  22.         }
  23.     ],
  24.     features : [{
  25.             recordCountKey : 'TotalRecordsCount',
  26.             pageIndexUrlKey : 'page',
  27.             pageSizeUrlKey : 'pageSize',
  28.             name : 'Paging',
  29.             type : 'local'
  30.         }, {
  31.             sortUrlKey : 'sort',
  32.             sortUrlKeyAscValue : 'asc',
  33.             sortUrlKeyDescValue : 'desc',
  34.             name : 'Sorting',
  35.             type : 'local'
  36.         }, {
  37.             filterExprUrlKey : 'filter',
  38.             filterLogicUrlKey : 'filterLogic',
  39.             name : 'Filtering',
  40.             type : 'local'
  41.         }
  42.     ],
  43.     height : '600px',
  44.     dataSource : "@Url.Action("Employees")",
  45.     responseDataKey : 'd'
  46. });

And the result of this is:

An empty Unbound column but still with appropriate filter conditions based on the defined data type.

As you can see you get a column of whatever type you have defined and the filter criteria match that too.

ASP.NET MVC

The helpers provided with the product could always be used instead to help you generate the same output with great ease. You actually have two options when using the server side assembly – chaining syntax with the HTML helper alone or combined with the grid model. Let’s see how that’s done. With just the helper in which case the model passed by the controller is the actual data:

  1. @using Infragistics.Web.Mvc
  2. @model IQueryable<GridUnboundColumn.Models.EmployeeDTO>
  1. @(Html.Infragistics().Grid(Model).AutoGenerateColumns(false).AutoGenerateLayouts(true)
  2.     .Columns(column =>
  3.     {
  4.         column.For(x => x.BusinessEntityID).HeaderText("B.E. Id").DataType("number");
  5.         column.For(x => x.SickLeaveHours).HeaderText("Sick Leave Hours").DataType("number");
  6.         column.For(x => x.VacationHours).HeaderText("Vacation Hours").DataType("number");
  7.         column.Unbound("empty").HeaderText("Empty").DataType("number");
  8.     })
  9.     .Height("600px")
  10.     .Features(feature =>
  11.         {
  12.             feature.Paging().Type(OpType.Local);
  13.             feature.Sorting().Type(OpType.Local);
  14.             feature.Filtering().Type(OpType.Local);
  15.             feature.GroupBy().Type(OpType.Local);
  16.         })
  17.     .DataBind()
  18.     .Render()
  19. )

Or using the grind model where the latter is being passed to the view:

  1. using Infragistics.Web.Mvc
  1. publicActionResult Demo()
  2. {
  3.     ViewBag.Message = "Below the grid ....";
  4.     AdventureWorksEntities context = newAdventureWorksEntities();
  5.     var employees = context.Employees.ToList().ConvertAll(e => e.ToDTO());
  6.  
  7.     GridModel grid = newGridModel();
  8.     grid.AutoGenerateColumns = false;
  9.     grid.Columns = newList<GridColumn>();
  10.     grid.Columns.Add(newUnboundColumn()
  11.     {
  12.         Key = "empty",
  13.         HeaderText = "Empty",
  14.         DataType = "number"
  15.     });
  16.     grid.Columns.Add(newGridColumn()
  17.     {
  18.         Key = "BusinessEntityID",
  19.         HeaderText = "B.E. Id",
  20.         DataType = "number"
  21.     });
  22.     grid.Columns.Add(newGridColumn()
  23.     {
  24.         Key = "SickLeaveHours",
  25.         HeaderText = "Sick Leave Hours",
  26.         DataType = "number"
  27.     });
  28.  
  29.     grid.Columns.Add(newGridColumn()
  30.     {
  31.         Key = "VacationHours",
  32.         HeaderText = "Vacation Hours",
  33.         DataType = "number"
  34.     });
  35.     grid.DataSource = employees.AsQueryable();
  36.     return View(grid);
  37. }

And that is merely consumed in the view:

  1. @using Infragistics.Web.Mvc
  2. @Html.Infragistics().Grid(Model)

Note: While the result will generally be the same, the above grid model is missing feature definitions just for this snippet. Also all snippets so far assume you are aware scripts need to be loaded in advance either manually or using the Infragistics resource loader widget. For full code refer to the demos themselves or general help with proper setup can be found in the Deployment Guide in our online help.

“So now how do I get data in there?”

That’s the logical question that follows. You have a column completely unrelated to your data model but it’s empty and that’s not why it’s there. Rest assured, you are quire in luck when it comes to options to supply values for those type of columns.

Unbound Values

So now comes the part I mentioned with model serialization and DTOs. The simple task of displaying AdventureWorks (AdventureWorks Sample Databases (MSDN)) Employees that has no names also requires the Person table. For the sake of the example imagine you don’t have access to the model OR those are in separate databases even. So the grid is bound to the ‘Employees’ and since I only want the names from ‘People’ I provide them in a separate list.

ASP.NET MVC

  1. publicActionResult Mvc()
  2. {
  3.     ViewBag.Message = "Below the grid ....";
  4.     AdventureWorksEntities context = newAdventureWorksEntities();
  5.     var employees = context.Employees.ToList().ConvertAll(e => e.ToDTO());
  6.     ViewBag.people = context.People.Select(p => p.FirstName + " " + p.LastName).ToList();
  7.     return View(employees.AsQueryable());
  8. }

Keep in mind the list can be absolutely anything, really from values from remote APIs to calculated or generated values. Next in the view we have same grid definition as above with the addition of using the ‘UnboundValues’ option:

  1. column.Unbound("name").UnboundValues((ViewBag.people asList<string>).Cast<object>().ToList()).HeaderText("Name").DataType("string");

And using the model (you can see the :

  1. grid.Columns.Add(newUnboundColumn()
  2. {
  3.     Key = "name",
  4.     HeaderText = "Name",
  5.     UnboundValues = context.People.OrderBy(p => p.BusinessEntityID)
  6.         .Select(p => p.FirstName + " " + p.LastName)
  7.         .ToList().Cast<object>().ToList(),
  8.     DataType = "string"
  9. });

Of course, those server side properties are proxy to the client API where you can use it like so:

  1. var peopleNames = @Html.Raw(Json.Encode(ViewBag.people)) ;
  1. {
  2.                 unbound : true,
  3.                 key : 'name',
  4.                 unboundValues : peopleNames,
  5.                 headerText : 'Name',
  6.                 dataType : 'string'
  7.             },

Ignite UI jQuery Grid with Unbound column of names from a separate source.

Note: When using the ‘UnboundValues’ property the list of provided values is assigned as is – meaning is the same order, which means that those *must* be ordered the same way as the data source if you want meaningful results.

Set Unbound Values

The very same result can be achieved using the ‘SetUnboundValues’ grid method. With it you can provide values for any columns by specifying its key:

  1. grid.Columns.Add(newUnboundColumn()
  2. {
  3.     Key = "name",
  4.     HeaderText = "Name",
  5.     DataType = "string"
  6. });
  7. grid.SetUnboundValues("name", peopleNames);

The method is also available with chaining and it has two overloads – one accepting as second parameter either a value list like so far or a dictionary with primary key and value pairs. The latter naturally requires your grid to have a primary key defined and if that is the case it is the recommended method to use as it’s the end result of the other overload anyway (yes, by all means, read that as performance gain). This can also spare you the hassle of having to order your sources the same way because using the Dictionary overload you can essentially assign value for each primary key. So for the above ‘peopleNames’ you can now use something like:

  1. var peopleNames = context.People.ToDictionary(x => (object)x.BusinessEntityID, x => (object)(x.FirstName + " " + x.LastName));

NOTE: While the unbound column itself may not support some remote feature (see below), other columns normally would and their results affect the entire data view and for correct results you absolutely need a proper mapping between unbound values and the grid’s primary key, therefore the above Dictionary overload of the ‘SetUnboundValues’ method is always the best option to be used.

As always things in client-side script are much less constrained and also somewhat different in functionality as it’s executed after the control is initialized. You can do that either in grid events or user triggered actions. For a start the easiest way is to go for a button click:

  1. <buttononclick="$('#Grid1').igGrid('setUnboundValues', [ 'John Doe(1)' , 'John Doe(2)' , 'Jane Doe(1)', 'John Doe(3)'], 'name')"> Set Unbound values </button>

Note: The client version of the method as you can see takes values first and then the key and can be called after the grid is initialized regardless of the unbound column state – be it still empty or already initialized with values, so this can be combined with the previous resulting in a prefilled on the server column and just the first few values replaced by the method:

The client side method to set unbound column values in the Ignite Ui grid.

Formula

The probably most exciting part. It’s not really even a formula per se. It’s a JavaScript function that is called with the rest of the row data and reference to the grid so it can calculate and return a value for the unbound column. However, since it’s just a client function you can return pretty much anything and obtain it in any way. That is powerful. Here’s the most basic usage I mentioned making some kind of row summary if you will. Inside the grid column definitions:

  1. {
  2.                 unbound : true,
  3.                 key : 'total',
  4.                 headerText : 'Total',
  5.                 dataType : 'number',
  6.                 formula : calcTotal
  7.             }
  1. function calcTotal(row, grid) {
  2.     return row.SickLeaveHours + row.VacationHours;
  3. };

Note that the row is pretty much the current data record even with the ‘name’ unbound value we already have. Also the formula property can be set with string, which I’m sure you will see when using the identical MVC property:

  1. grid.Columns.Add(newUnboundColumn()
  2. {
  3.     Key = "total",
  4.     HeaderText = "Total",
  5.     DataType = "number",
  6.     Formula = "calcTotal"
  7. });

The result of this is the heading screenshot of course.

Gotcha-s

As I explained it’s a deceptively simple feature and once you look into it it’s quite something. That warrants a few limitations. While it does work just fine on the client with every feature, the server side compatibility is lacking – in fact, remote Filtering, Sorting and GroupBy are not supported for unbound columns. You could potentially perform updating but as you can imagine the rows (grid records) no longer match the server model the grid was bound to so custom deserialization would be required.

There’s also the matter of how the additional data is to be handled and that is dictated by the ‘mergeUnboundColumns’ property. There are some performance implications to this that we’ll leave for some other time (read: the second blog, find directs links to skip to that below).

Resources and demos

Donwload your Ignite UI Free Trial now!

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

TL;DR | Summary

We’ve looked into the benefits and different applications of the all new Unbound Columns feature in the Ignite UI jQuery Grid. We’ve went through adding such a column to the grid and the different ways to provide it with data in both client-side scripts and using the ASP.NET MVC server-side helper/wrapper. We noted some cases to in mind and in a following blog we’ll go into greater detail about the ‘mergeUnboundColumns’ property and also methods to get values from unbound columns.

jQuery Grid Unbound Column: How to make the most of it

$
0
0

Ignite UI jQuery Grid's Unbound Columns and configuring it to make the most of it.When in need of displaying columns with separate data – be it from a different source or based on the values of others – this new column type will prove truly helpful. Be sure to read the introductory blog on how to get started with jQuery Grid Unbound Columns. As promised we’ll go into greater detail into the options available to tweak the Ignite UI Grid’s Unbound Column feature to your own use case and needs.

Merge Unbound Columns

This is where it really gets interesting. Due to the very nature of the performed functions – bringing two or more distinct sources together in a single control’s view -  as a developer you are given a additional option to control that. You can define the grid’s behavior  using the MergeUnboundColumns property. It is relevant when defining a grid on  the server-side with the ASP.NET MVC wrapper. The default option is false and what happens is that your unbound values get sent to the client as Metadata, rather than along with the actual data source records. You can take for example the previous blog sample. To make it clear here is how a part of the grid definition would look with the option off by default :

Ignite UI jQuery Grid definition as produced by the ASP.NET MVC wrapper. MergeUnboundCoulmns is false by default and all the unbound values are transmitted to the client in the source metadata.

(It’s a little bit formatted and the actual records are collapsed so the structure can be visible, but rest assured they do not contain any of the unbound values)

Of course, if you take a closer look at the generated scripts you will also notice the above grid being initialized with the identical client property ‘mergeUnboundColumns’ being false. I did say it was a property meaningful when using the ASP.NET MVC helpers and it mostly is. What the client property does is being a flag for the grid if the data comes merged..or should the control do that on the client-side only in combination with remote data source. Otherwise, this property with local source won’t have any effect whatsoever. What all this means is that at the end of the journey the widget will have to do the merging anyway – that is how such values can normally participate in features such as sorting, filtering, etc.

Naturally, what makes the difference when setting this property to true is that the merging is performed on the server. Notice line 2 below:

  1. @(Html.Infragistics().Grid(Model).AutoGenerateColumns(false).AutoGenerateLayouts(true)
  2.     .MergeUnboundColumns(true)
  3.     .Columns(column =>
  4.     {
  5.         column.For(x => x.BusinessEntityID).HeaderText("B.E. Id").DataType("number");
  6.         column.Unbound("name").HeaderText("Name").DataType("string");
  7.         column.For(x => x.LoginID).HeaderText("Login Id").DataType("string");
  8.         column.For(x => x.SickLeaveHours).HeaderText("Sick Leave Hours").DataType("number");
  9.         column.For(x => x.VacationHours).HeaderText("Vacation Hours").DataType("number");
  10.     })
  11.     .Height("600px")
  12.     .Features(feature =>
  13.         {
  14.             feature.Paging().Type(OpType.Local);
  15.             feature.Sorting().Type(OpType.Local);
  16.             feature.Filtering().Type(OpType.Local);
  17.             feature.GroupBy().Type(OpType.Local);
  18.         })
  19.     .SetUnboundValues("name", (ViewBag.people asList<string>).Cast<object>().ToList())
  20.     .DataBind()
  21.     .Render()
  22. )

As a result data gets send as a totally normal set of records and the merge client property states true telling the widget not to look for values in the metadata as they are no longer there:

Ignite UI jQuery Grid definition as produced by the ASP.NET MVC wrapper. MergeUnboundCoulmns is true and unbound values are now part of the data records.

Merged Unbound columns distinctive property – unboundDS

The “unboundDS” property indicates that the column is indeed unbound. This is done, regardless of the client treating unbound columns as bound when ‘mergeUnboundColumns’ is true. Or should I say this is done exactly because of that. Have a look at what comes as a column definition in this case:

  1. {
  2.                 unbound : false,
  3.                 key : 'name',
  4.                 headerText : 'Name',
  5.                 dataType : 'string',
  6.                 unboundDS : true
  7.             }

As the values for the column are already in the records the grid is not supposed to look for them elsewhere and the unbound setting is false. But then again the same false can be added to any other column with no side effects..So how do you know if there are actual unbound columns defined anyway? This is why the ‘unboundDS’ property is here for, to give a way for a client code to know if and which columns’ values are separate and most importantly to inform the grid that remote operations such as filtering and sorting (not supported by the feature) should not be allowed for that column.

Performance

As you can  see the above-described configuration produces mostly the same results, however this time the served had to do the heavy lifting and the client enjoys not having anything to do with this and being blissfully unaware of unbound values. This is also very important as you have the option to distribute the load between client and server. Then again a fair warning follows – merging on either side may result in a performance hit if the original data source is way too big. This is because the whole data must be traversed and new unbound values added to it (or default null values if the list values are less). As you can imagine though, the server is usually the one is a more dangerous position as the large data manipulations can become pretty a serious problem when they have to be performed multiple times for multiple clients. A client doing this instead should take much less of an impact doing just its own work.

On a side note, remember the Set Unbound Values methods discussed in the previous blog and how it is strongly advisable to use the Dictionary overload as it is a performance gain? Well it’s even more true when using the merge option on the server - causes even more work as the server will be otherwise forced to create the bindings between values you provide and primary keys available. Of course, you are best to save it the trouble and assign them yourself, plus that way the order of the values won’t matter as much.

Client-side data manipulation

Last time we demonstrated the client-side version of the method to set unbound values and saw the additional route of applying a formula on the client. However, those are not the only ways to set unbound values. There’s also an interesting turn of events regarding the usage of the so far described merge functionality that requires some additional client work and therefore, being well equipped to manipulate data on the client is never a bad thing. 

Merging Unbound Columns and setting values on data-bound client event

Let’s have an identical example as so far but this time lets enable the merge option along with using the formula field from last time. Nothing special it shouldn’t affect the end result, right?

  1. @(Html.Infragistics().Grid(Model).AutoGenerateColumns(false).AutoGenerateLayouts(true)
  2.     .MergeUnboundColumns(true)
  3.     .Columns(column =>
  4.     {
  5.         column.For(x => x.BusinessEntityID).HeaderText("B.E. Id").DataType("number");
  6.         column.Unbound("name").HeaderText("Name").DataType("string");
  7.         column.For(x => x.LoginID).HeaderText("Login Id").DataType("string");
  8.         column.For(x => x.SickLeaveHours).HeaderText("Sick Leave Hours").DataType("number");
  9.         column.For(x => x.VacationHours).HeaderText("Vacation Hours").DataType("number");
  10.         column.Unbound("total").HeaderText("Total").DataType("number").Formula("calcTotal");
  11.     })
  12.     .Height("600px")
  13.     .Features(feature =>
  14.         {
  15.             feature.Paging().Type(OpType.Local);
  16.             feature.Sorting().Type(OpType.Local);
  17.             feature.Filtering().Type(OpType.Local);
  18.             feature.GroupBy().Type(OpType.Local);
  19.         })
  20.     .SetUnboundValues("name", (ViewBag.people asList<string>).Cast<object>().ToList())
  21.     .DataBind()
  22.     .Render()
  23. )

Well it did…

Ignite UI jQuery Grid with two unbound columns (merked orange). This shwos the result of using the merge property with a column formula and since values are not available on the server default null-s are assigned and the client never looks for values and the formula function is left uncalled.

See when merge is enabled essentially you tell the grid values will be provided on/from the server and a JavaScript function for the formula is definitely not that. Also since as I explained values are assigned with null as default… that is what you end up getting this way. Can you still provide the same functionality with merged unbound columns? Sure! It’s not quite as simple, but possibly the most appropriate way to do this is to handle the client ‘dataBound’ event, at which point will have the data source object ready and you can directly assign values for unbound columns. Here’s the old formula function:

  1. function calcTotal(row, grid) {
  2.     return row.SickLeaveHours + row.VacationHours;
  3. };

All that’s different is that you don’t have a function called each time with row values, but you can make it work again(if it was something big that you would not want to change or refactor):

  1. $("#Grid1").live("iggriddatabound", function (evt, ui) {
  2.    // ui.owner is a reference to the actual grid widget
  3.     var data = ui.owner.dataSource.data();
  4.     $.each(data, function (index, record) {
  5.         var unboundValue = calcTotal(record, ui.owner);
  6.         record["total"] = unboundValue;
  7.     });
  8. });

Once more, the data-bound event is the most logical place to execute that code as you have the data source, but the UI is not yet rendered, therefore this solution creates exactly the same results as before without noticeable difference for the end-user. Even so, it’s worth mentioning that you can enforce this method of assigning unbound values directly into the source at any point after this as an alternative to the SetUnboundValues method.

Get them!

Speaking of ‘SetUnboundValues ‘ the opposite operation is also available as a client method. There’s also a method to get the whole column even:

  1. var unboundColumn = $('#grid1').igGrid('getUnboundColumnByKey', 'name');
  2. var unboundValues = $('#grid1').igGrid('getUnboundValues', 'total');

The first provides you with the whole column with it’s properties and values:

The results returned by the client method to get Unbound column in the Ignite UI Grid.

And the values only method will give you just that array of values, however the column key parameter is optional and if not defined an object with all unbound columns’ value arrays will be returned instead.

The results returned by the client method to get values for Unbound column in the Ignite UI Grid. 

It’s Complicated

Or is it? There are a number of questions in regard to what happens with unbound columns and advanced column features you’d normally use. This is especially true when you have a formula defined as well… well you can rest assured the functionality is all there. In the very special case of formula calculations to get the value – it is called before any formatting function or applying a template so for all intents and purposes it will work as with any other column. Just the column definition:

  1. column.Unbound("total").HeaderText("Total").DataType("number").Formula("calcTotal").Template("<span {{if parseInt(${total})/100 > 1 }} class='red' {{/if}}> ${total} / 100 </span>");

And it’s all working pretty well:

Ingite UI jQuery Grid with an Unbound column with a values calculated with a formula and conditional templating applied.

Same goes for formatter functions that are the more extended functionality, but largely similar to templating.

Resources and demos

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Donwload your Ignite UI Free Trial now!

TL;DR | Summary

In this article we covered the usage of the Merge Unbound Columns property and its vary important performance implications. A look at the inner workings of the grid’s server model provided some insight on what’s really happening and how that would affect your application. Furthermore, we saw how the merge can affect functionality such as the formula and how to overcome that, along with some additional client-side manipulation. Hopefully the blog also cleared any possible questions as to whether Unbound columns work well with other  Ignite UI Grid column options such as formatter functions or templates.

I’ll end this in a manner similar to how it was started – saying that such a feature concerning something as underlying as columns types in the jQuery Grid can affect multiple behaviors and features and is by a long shot not a simple functionality, as I hope it has been made clear by now. For that reason, there are still more interesting topics to be discussed for the future.


IgniteUI jQuery Grid Unbound Columns: Updating and Hierarchy

$
0
0

igniteui-jquery-grid-unbound-columns-updating-hierarchyUnbound Columns bring you the freedom of flexible layouts in the IgniteUI Grid and neat support for features to make your grid behave like nothing’s that different.  After you’ve seen some of the capabilities (getting started blog) and inner workings(configure and make the most of it) it’s only natural to tackle some not too basic cases. We’ll throw in some editing action – in all fairness, something very basic as far as the user or desired end-result are concerned, however nothing simple in the eyes of the developer. Following that some curious scenarios when using the Hierarchical Grid with unbound columns. So let’s jump right on topic.

To Save or not to Save

Custom Deserialization

Adding Updating should be fairly straight-forward, but there’s a catch.. or rather a few. One thing you are extremely likely to face is handling updates to the server. The grid will behave as expected and kindly include unbound values in the transactions. For that reason, as we already mentioned. the client-side model that includes Unbound columns would not match the one the grid was bound to on the server. That means direct deserialization using the standard .NET serializators won't go very well(more like at all) and the same goes for using the Grid model's LoadTransactions method(which is the same thing really). The most reasonable way to go about this is custom deserialization. Don’t let that fool you – it’s actually very easy with something like Json.NET. Plus, it even comes pre-packed with some (all?) new MVC4 templates anyway. It’s fast and, as far as my experience goes, it handles missing or extra values easily. Let’s have the following Grid configuration with AdventureWorks (AdventureWorks Sample Databases (MSDN)) Employees similar to so far used examples:

  1. $('#grid').igGrid({
  2.     updateUrl : '/Home/UpdateUrl',
  3.     localSchemaTransform : false,
  4.     autoGenerateColumns : false,
  5.     autoGenerateLayouts : false,        
  6.     primaryKey: "BusinessEntityID",
  7.     columns : [{
  8.             key : 'BusinessEntityID',
  9.             dataType : 'number',
  10.             headerText : 'B.E. Id'
  11.         }, {
  12.             unbound : true,
  13.             key : 'name',
  14.             headerText : 'Name',
  15.             dataType : 'string'
  16.         }, {
  17.             key : 'LoginID',
  18.             dataType : 'string',
  19.             headerText : 'Login Id'
  20.         }, {
  21.             key : 'SickLeaveHours',
  22.             dataType : 'number',
  23.             headerText : 'Sick Leave Hours'
  24.         }, {
  25.             key : 'VacationHours',
  26.             dataType : 'number',
  27.             headerText : 'Vacation Hours'
  28.         }, {
  29.             unbound : true,
  30.             key : 'total',
  31.             headerText : 'Total',
  32.             dataType : 'number',
  33.             formula : calcTotal,
  34.             template : '<span {{if parseInt(${total})/100 > 1 }} class=\'red\' {{/if}}> ${total} / 100 </span>'
  35.         }
  36.     ],
  37.     features : [{
  38.             name : 'Updating',
  39.             enableAddRow : false,
  40.             columnSettings : [{
  41.                     columnKey : 'total',
  42.                     readOnly : true
  43.                 }, {
  44.                     columnKey : 'BusinessEntityID',
  45.                     readOnly : true
  46.                 }]
  47.         }
  48.     ],
  49.     height : '600px',
  50.     dataSource : "@Url.Action("HrcEmployees")",
  51.     rendered: function (evt, ui){
  52.         ui.owner.setUnboundValues(peopleNames , "name");
  53.     }
  54. });

Or the equivalent in ASP.NET MVC using the helper with chaining:

  1. @(Html.Infragistics().Grid(Model).AutoGenerateColumns(false).AutoGenerateLayouts(true)
  2.     .PrimaryKey("BusinessEntityID")
  3.     .Columns(column =>
  4.     {
  5.         column.For(x => x.BusinessEntityID).HeaderText("B.E. Id").DataType("number");
  6.         column.Unbound("name").HeaderText("Name").DataType("string");
  7.         column.For(x => x.LoginID).HeaderText("Login Id").DataType("string");
  8.         column.For(x => x.SickLeaveHours).HeaderText("Sick Leave Hours").DataType("number");
  9.         column.For(x => x.VacationHours).HeaderText("Vacation Hours").DataType("number");
  10.         column.Unbound("total").HeaderText("Total").DataType("number")
  11.             .Formula("calcTotal").Template("<span {{if parseInt(${total})/100 > 1 }} class='red' {{/if}}> ${total} / 100 </span>");
  12.     })
  13.     .Height("600px")
  14.     .Features(feature =>
  15.         {
  16.             feature.Updating().EnableAddRow(false).ColumnSettings(colSetting =>
  17.             {
  18.                 colSetting.ColumnSetting().ColumnKey("total").ReadOnly(true);
  19.                 colSetting.ColumnSetting().ColumnKey("BusinessEntityID").ReadOnly(true);
  20.             }).EditMode(GridEditMode.Row);
  21.         })
  22.     .UpdateUrl(Url.Action("UpdateUrl"))
  23.     .SetUnboundValues("name", (ViewBag.people asList<string>).Cast<object>().ToList())
  24.     .DataBind()
  25.     .Render()
  26. )

As you can see the ID is not editable for obvious reasons and so is the formula field. What you would get from the grid as a transaction would contain all of the above properties. One approach would be to take advantage of the flexibility of Json.Net and go about it as you normally would:

  1. var transactions = JsonConvert.DeserializeObject<List<Transaction<EmployeeDTO>>>(Request.Form["ig_transactions"]);

Form there on it is identical to what you can find in the documentation on Batch Updating. Another take would be to go for LINQ to JSON instead and deserialize to JObject:

  1. var transactions = JsonConvert.DeserializeObject<List<Transaction<JObject>>>(Request.Form["ig_transactions"]);

An advantage you get is that if you like me don’t have the whole model displayed in the grid and therefore the transactions don’t contain quite the whole records and assigning it blindly will just put nulls in the ones I’m missing. Besides picking separate properties from the record this approach is also the easiest way to access unbound values if you need them:

  1. AdventureWorksEntities context = newAdventureWorksEntities();
  2. foreach (Transaction<JObject> transaction in transactions)
  3. {
  4.     int id = (int)transaction.row["BusinessEntityID"];
  5.     // take id in advance as accessing it is not a canonical method and then find the data records
  6.     var employee = context.Employees.Where(x => x.BusinessEntityID == id).Single();
  7.     var person = context.People.Where(x => x.BusinessEntityID == id).Single();
  8.     if (transaction.type == "row") //edit!
  9.     {
  10.         employee.VacationHours = (short)transaction.row["VacationHours"];
  11.         employee.SickLeaveHours = (short)transaction.row["SickLeaveHours"];
  12.         employee.LoginID = (string)transaction.row["LoginID"];
  13.         person.FirstName = transaction.row["name"].ToString().Split(' ').First();
  14.         person.LastName = transaction.row["name"].ToString().Split(' ').Last();
  15.     }
  16.     if (transaction.type == "delete") //delete!
  17.     {
  18.         context.Employees.DeleteObject(employee);
  19.     }
  20. }
  21. context.SaveChanges();

“My Unbound values don’t reach the server”

So you use the client-side ‘setUnboundValues()’ method like you are supposed to and attempt to save the changes with the method above but they never appear to persist. What gives? Well, for starters “you are supposed to” in the last sentence is truly wrong – the method does set values but since it’s designed to work without updating on the grid, the changes it makes are “client-side only” type and are completely unrelated with Updating and transactions. That means that you are setting a value but it is never sent to the server or persisted in other way. So to achieve the desired result you should turn to the Updating API instead and use either ‘setCellValue’ or ‘updateRow’. Yes, they work work with Unbound columns! Frankly, the methods have very subtle differences with  the main one being the type of transaction they generate and I usually pick the one that matches the edit mode:

  1. <buttononclick="$('#grid').data('igGridUpdating').setCellValue(2, 'name', 'John Doe')"> Update names [cell]</button>
  2. <buttononclick="$('#grid').data('igGridUpdating').updateRow(2, { name : 'John Doe'})"> Update names [row]</button>

There basically produce the same result with different type of transaction. In the demo and the JFiddle versions linked below I’ve put up a simple demonstration of this with a log you can use to list the current transactions on the grid in order to see that there are indeed none after a ‘setUnboundValues()’ call but the Updating methods behave as expected.

Formula Trouble

So you save  your changes and everything obviously goes well with the server, but ..

igniteui-jquery-grid-unbound-columns-updating-formula

Find something wrong? This is a row in which I’ve edited the Vacation hours value, however the total value calculated from a formula stays the same and moreover, it’s the same after the actual submit of transaction to the server passes too. Because of the type of functionality formula property – it is neither a template nor a formatting function, it’s supposed to provide the initial value. Therefore, it’s called on binding and from there on the grid has data to work with as if it is part of the data source and formula is no longer needed. But hey, I want my value correct and I don’t feel like reloading the page for sure. So how do you get this to work? Consider this, the save changes method does not offer a callback and in case you are wondering neither does the original in the igDataSource. But it does say AJAX and it should have a callback, right? And it does, lets just say it’s well hidden. It’s called a success handler and the grid maintains an array of them to call when it handles the AJAX success (only!). Also the problem is I want to refresh my view in order to update the formula calculations. However, simply calling ‘dataBind()’ on the data source won’t do in my case as I have additional unbound values(the names) that are still the same and they won’t update until a refresh of the page. Plus, it's a lot of overhead I don't really need(goodness forbid using the grid's data bind method that does a whole lot more work even). Thankfully you have grid operations that cause a re-bind such as paging and sorting without messing with the actual data in the source. What if we could use that? Well we can! A slight hack if you will would be to trick the grid into changing page without actually changing it which would essentially be a page re-fresh. This can be done using the ‘pageIndex()’ data source method you'd normally use to programmatically change page or get current index:

  1. $("#grid").live("iggridrendered", function (event, ui) {
  2.     //rendered event only fired once so add success update handler here
  3.     ui.owner.dataSource._addChangesSuccessHandler(updateSuccess);
  4. });
  5. function updateSuccess(data, textStatus, jqXHR) {
  6.     if (data.Success === true&& textStatus == "success") {
  7.         //'re-bind' to force formula calculation with new verified values.
  8.         var gridDS = $("#grid").data("igGrid").dataSource;
  9.         gridDS.pageIndex(gridDS.pageIndex());
  10.     }
  11. }

This registers your own handler that gets the very same parameters as a normal AXAJ callback (and this can be useful in many other scenarios!) and in it we access the grid’s data source now that we know that the save has gone through and do our little refresh inception-style. It’s not perfect, but it kind of works; another very plausible solution would be to just go ahead and set the right value.

NOTE: Keep in mind that if you use template to display value in unbound column that is built with values from other columns it will update once they change. There’s a demo available for that in both MVC and JFiddle below.

Hierarchical Grid with Unbound Columns

Now here’s another good one. By design the hierarchical grid uses flat igGrids and for that reason we consider most of the functionality  to be shared and used in the same way with the same results. However, there are a few logical, yet important differences to consider when using hierarchy with Unbound Columns.

Setting values and child column layouts

Of course, all the methods to do that are still available, but consider this – you now have not just one grid, but rather a whole bunch of them. Using the ‘unboundValues’ is mostly logical as they are tied to their column definitions. But when you decide to set some unbound values on the client but nothing happens? For reference here’s how the grid definition looks:

  1. $('#grid').igHierarchicalGrid({
  2.     dataSource: @Html.Raw(Json.Encode(Model)),
  3.     dataSourceType: 'json',
  4.     autoGenerateColumns: false,
  5.     autoGenerateLayouts: false,
  6.     columns: [{
  7.         key: 'DepartmentID',
  8.         headerText: 'DEP Id',
  9.         dataType: 'number'
  10.     }, {
  11.         key: 'Name',
  12.         headerText: 'Name',
  13.         dataType: 'string'
  14.     }, {
  15.         key: 'GroupName',
  16.         headerText: 'Group Name',
  17.         dataType: 'string'
  18.     }, {
  19.         key: 'ModifiedDate',
  20.         headerText: 'Modified Date',
  21.         dataType: 'date'
  22.     }, {
  23.         unbound: true,
  24.         key: 'employees',
  25.         headerText: 'Employees',
  26.         dataType: 'number',
  27.         unboundValues: [7, 3, 18, 9, 12, 4, 180, 5, 6]
  28.     }
  29. ],
  30.     columnLayouts: [{
  31.         autoGenerateColumns: false,
  32.         autoGenerateLayouts: false,
  33.         mergeUnboundColumns: true,
  34.         columns: [{
  35.             key: 'BusinessEntityID',
  36.             headerText: 'B.E. Id',
  37.             dataType: 'number'
  38.         }, {
  39.             key: 'DepartmentID',
  40.             headerText: 'Dep id',
  41.             dataType: 'number',
  42.             hidden: true
  43.         }, {
  44.             unbound: true,
  45.             key: 'name',
  46.             headerText: 'name',
  47.             dataType: 'string'
  48.         }, {
  49.             key: 'VacationHours',
  50.             headerText: 'Vacation Hours',
  51.             dataType: 'number'
  52.         }, {
  53.             key: 'SickLeaveHours',
  54.             headerText: 'Sick Leave Hours',
  55.             dataType: 'number'
  56.         }, {
  57.             unbound: true,
  58.             key: 'counter',
  59.             headerText: 'Counter',
  60.             dataType: 'number'
  61.         }
  62.         ],
  63.         key: 'Employees',
  64.         primaryKey: 'BusinessEntityID',
  65.         foreignKey: 'DepartmentID'
  66.     }
  67. ],
  68.     height: '500px',
  69.     autoCommit: false,
  70.     primaryKey: 'DepartmentID',
  71.     localSchemaTransform: true
  72. });

igniteui-jquery-hierarchical-grid-unbound-columns

And to sill some of the missing values you attempt calling something like:

  1. $('#grid').igHierarchicalGrid('setUnboundValues', [ 52, 522, 368, 42635], 'counter');

However nothing happens. If you had attempted the same with say ‘employees’ it would’ve. That is because as we explained there are multiple grids here and set value methods are to be called for the respective layout. So in order to add values to the child layout you need to access it first. The hierarchical grid provides a method to do that:

  1. $.each($('#grid').data('igHierarchicalGrid').allChildrenWidgets(), function (i, grid) {
  2.     grid.setUnboundValues([12.2, 12.2, 12.2, 12.2], 'counter');
  3. });

You could pick one, but above I’m targeting all available child grids and setting values and there’s a good reason. To be able to target specific layout you need to be aware it is there – the Hierarchical grid creates the child grids only when expanding for performance reasons, therefore you cannot set values to something that doesn’t exist yet. Furthermore, unless you provide you unbound values in advance ( set them on the column property or merge them on the server) even after calling the above, newly expanded(created) layouts will still be with empty unbound columns. My best advice is to assign even temporary initial values to spare the user the sight of empty columns and/or handle a suitable event such as 'childGridCreated'.

ASP.NET MVC

MVC offers some additional challenges such as the relevant Merge Unbound Columns property and others. First and foremost let me remind again the SetUnboundValues’s Dictionary method overload is still your best bet to get things going as expected. Also the client-side explanations above still holds true although this time you set values to column layouts of the main grid, but it has to be done for the respective layout.

  1. childLayout.MergeUnboundColumns = true;
  2. childLayout.Columns.Add(newUnboundColumn()
  3. {
  4.     Key = "counter",
  5.     HeaderText = "Counter",
  6.     DataType = "number",
  7.     UnboundValues = newList<object>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 }
  8. }); //This will not work!

Using the Unbound values column property which if used in child layout definition will be applied to all layouts by default. However when you enable merging the values inside the column definition are not supported and pretty much ignored. So in their place you get the default null values and empty columns on the client.

Then you have the SetUnboundValues’s List overload that suspiciously looks very much like the property, but you will be surprised when it actually works with merging. But there’s also a catch – since the method’s purpose is to provide all values for for a grid in one go, when used it will still map values to ID’s ..of the first child layout only. This is as far as the grid is willing to do the guessing for you I guess. However if the child layout doesn’t have primary key another default behavior kicks in and those values are applied separately to each child. I’m only explaining all of this as I think in some situations child layouts could be sharing values or just need an example values for one layout and these can turn out just fine.

Once again, assigning with the Dictionary overload applies the required mapping so values will properly be added throughout children  based on key matches.

Load On Demand and Merge Unbound columns don’t go well…

These really don’t go well at all! And it’s really not that surprising – LoadOnDemand assures the grid that data for child layouts will be provided later on and merging demands they are present in the data source, so even though you may have Unbound Values set using the property itself and you can see it on the client code it will still be ignored and data for the unbound columns will be required from the data received when creating the child. That mean that you need to send it along with the original grid model and this makes the Unbound Columns redundant or if the source is oData nearly absurd to implement. SO better don’t try this at home. The best part is that if you don’t provide those values and the grid starts looking for them it will end up badly..with exceptions :) This is true for SetUnboundValue methods as well, however this is not hopeless combination of functionality:

GetData() to the rescue

Of course, this is the Grid models’ GetData() method and it’s extremely helpful when using load on demand. In fact, so helpful, that when used to generate responses for the child layouts it will look at your model and properly merge unbound values applied with SetUnboundValue methods again following the default  behavior – with list values are added to each generated response essentially added to very layout and with dictionary overload just the matching ones. So this (lines 73/74 in the model show the setting of values with both List and Dictionary):

  1. publicJsonResult BindChild(string path, string layout)
  2. {
  3.     GridModel viewModel = GenerateGridModel();
  4.     AdventureWorksEntities context = newAdventureWorksEntities();
  5.     viewModel.DataSource = context.Employees.ToList().ConvertAll(eh => eh.ToDTO()).AsQueryable();
  6.     return viewModel.GetData(path, layout);
  7. }   
  8.  
  9. privateGridModel GenerateGridModel()
  10. {
  11.     GridModel grid = newGridModel();
  12.     grid.Height = "500px";
  13.     grid.AutoCommit = false;
  14.     grid.PrimaryKey = "DepartmentID";
  15.    
  16.     grid.AutoGenerateColumns = false;
  17.     grid.AutoGenerateLayouts = false;
  18.     GridColumnLayoutModel childLayout = newGridColumnLayoutModel();
  19.  
  20.     //settings
  21.     grid.LoadOnDemand = true;
  22.     childLayout.Key = "Employees";
  23.     childLayout.PrimaryKey = "BusinessEntityID";
  24.     childLayout.ForeignKey = "DepartmentID";
  25.     childLayout.MergeUnboundColumns = true;
  26.     
  27.     childLayout.AutoGenerateColumns = false;
  28.     childLayout.AutoGenerateLayouts = false;
  29.  
  30.     childLayout.DataSourceUrl = Url.Action("BindChild");
  31.  
  32.     childLayout.Columns = newList<GridColumn>();
  33.     childLayout.Columns.Add(newGridColumn()
  34.     {
  35.         Key = "BusinessEntityID",
  36.         HeaderText = "B.E. Id",
  37.         DataType = "number"
  38.     });
  39.  
  40.     childLayout.Columns.Add(newGridColumn()
  41.     {
  42.         Key = "DepartmentID",
  43.         HeaderText = "Dep id",
  44.         DataType = "number",
  45.         Hidden = true
  46.     });
  47.  
  48.     childLayout.Columns.Add(newUnboundColumn()
  49.     {
  50.         Key = "name",
  51.         HeaderText = "name",
  52.         DataType = "string",
  53.     });
  54.  
  55.     childLayout.Columns.Add(newGridColumn()
  56.     {
  57.         Key = "VacationHours",
  58.         HeaderText = "Vacation Hours",
  59.         DataType = "number"
  60.     });
  61.     childLayout.Columns.Add(newGridColumn()
  62.     {
  63.         Key = "SickLeaveHours",
  64.         HeaderText = "Sick Leave Hours",
  65.         DataType = "number"
  66.     });
  67.     childLayout.Columns.Add(newUnboundColumn()
  68.     {
  69.         Key = "counter",
  70.         HeaderText = "Counter",
  71.         DataType = "number"
  72.     });
  73.     childLayout.SetUnboundValues("counter", newList<object>() { 1, 2, 3, 4, 5, 6, 7, 8, 9 });
  74.     childLayout.SetUnboundValues("name", peopleNames);
  75.  
  76.     grid.Columns = newList<GridColumn>();
  77.     grid.Columns.Add(newGridColumn()
  78.     {
  79.         Key = "DepartmentID",
  80.         HeaderText = "DEP Id",
  81.         DataType = "number"
  82.     });
  83.     grid.Columns.Add(newGridColumn()
  84.     {
  85.         Key = "Name",
  86.         HeaderText = "Name",
  87.         DataType = "string"
  88.     });
  89.  
  90.     grid.Columns.Add(newGridColumn()
  91.     {
  92.         Key = "GroupName",
  93.         HeaderText = "Group Name",
  94.         DataType = "string"
  95.     });
  96.  
  97.     grid.Columns.Add(newGridColumn()
  98.     {
  99.         Key = "ModifiedDate",
  100.         HeaderText = "Modified Date",
  101.         DataType = "date"
  102.     });
  103.     grid.Columns.Add(newUnboundColumn()
  104.     {
  105.         Key = "employees",
  106.         HeaderText = "Employees",
  107.         DataType = "number",
  108.         UnboundValues = newList<object>() { 7, 3, 18, 9, 12, 4, 180, 5, 6 }
  109.     });
  110.  
  111.     grid.ColumnLayouts.Add(childLayout);
  112.     return grid;
  113. }

Will produce the following response to the client:

  1. {
  2.     "Records" : [{
  3.             "BusinessEntityID" : 11,
  4.             "DepartmentID" : 2,
  5.             "name" : "Ovidiu Cracium",
  6.             "VacationHours" : 7,
  7.             "SickLeaveHours" : 23,
  8.             "counter" : 1
  9.         }, {
  10.             "BusinessEntityID" : 12,
  11.             "DepartmentID" : 2,
  12.             "name" : "Thierry D\u0027Hers",
  13.             "VacationHours" : 9,
  14.             "SickLeaveHours" : 24,
  15.             "counter" : 2
  16.         }, {
  17.             "BusinessEntityID" : 13,
  18.             "DepartmentID" : 2,
  19.             "name" : "Janice Galvin",
  20.             "VacationHours" : 8,
  21.             "SickLeaveHours" : 24,
  22.             "counter" : 3
  23.         }
  24.     ],
  25.     "TotalRecordsCount" : 0,
  26.     "Metadata" : {
  27.         "timezoneOffset" : 10800000
  28.     }
  29. }

Even when merging is not in use, this method will be kind enough to include the said values as metadata instead and achieve the same results. One thing to note is that the method does require the presence of primary and foreign key in the child layout in order to perform proper mapping, which if not needed can be hidden but still must be present. Despite that,  using the Grid model with Load On demand is the best way to have child data generated properly and it allows Unbound Columns to be merged along with all else!

Bonus scene (formula + template)

Or more like bonus scenario. Since unbound columns are not even tied with values what happens if as above and examples before you got that Unbound Column with both formula to calculate total and template. Now that template uses a static value for the max…how about we add yet another column for that, provide some values and use that instead in the template?

  1. @(Html.Infragistics().Grid(Model).AutoGenerateColumns(false).AutoGenerateLayouts(false).LocalSchemaTransform(false)
  2.     .ColumnLayouts(layout => layout.For(x => x.Employees).AutoGenerateColumns(false).Columns(column =>
  3.         {
  4.             column.For(x => x.BusinessEntityID).HeaderText("B.E. Id").DataType("number");
  5.             column.For(x => x.SickLeaveHours).HeaderText("Sick Leave Hours").DataType("number");
  6.             column.For(x => x.VacationHours).HeaderText("Vacation Hours").DataType("number");
  7.             column.Unbound("total").HeaderText("Total / Max").DataType("number").Formula("calcTotal").Template("<span {{if ${total}/parseInt(${max}) > 1 }} class='red' {{/if}}> ${total} / ${max} </span>");
  8.             column.Unbound("max").HeaderText("Max").DataType("string").Hidden(true);
  9.         })
  10.         .LocalSchemaTransform(false)
  11.         .SetUnboundValues("max", newDictionary<object, object>() { { 2, 50 }, { 3, 100 }, { 5, 80 }, { 6, 100 }, { 11, 25 }, { 12, 66 }, { 13, 76 }, { 4, 32 }, { 15, 18 } })
  12.         .PrimaryKey("BusinessEntityID")
  13.     )
  14.     .PrimaryKey("DepartmentID")
  15.     .Columns(column =>
  16.     {
  17.         column.For(x => x.DepartmentID).HeaderText("DEP Id").DataType("number");
  18.         column.For(x => x.Name).HeaderText("Name").DataType("string");
  19.         column.For(x => x.GroupName).HeaderText("Group Name").DataType("string");
  20.         column.For(x => x.ModifiedDate).HeaderText("Modified Date").DataType("date");
  21.         column.Unbound("employees").HeaderText("Employees").DataType("number").UnboundValues(newList<object>() { 7, 3, 18, 9, 12, 4, 180, 5, 6 });
  22.     })
  23.     .Height("600px")
  24.     .Features(feature =>
  25.         {
  26.             feature.Paging().Type(OpType.Local).PageSize(10);
  27.             feature.Sorting().Type(OpType.Local);
  28.             feature.Filtering().Type(OpType.Local);
  29.         })
  30.     .DataSource(Model)
  31.     .ClientDataSourceType(ClientDataSourceType.JSON)
  32.     .DataBind()
  33.     .Render()
  34. )

Would it work as expected? For a moment, it might. See there’s no merging enabled, therefore it happens on the client and since the schema doesn't include the Unbound columns the grid isn’t as kind as to go about and apply null-s to every record missing unbound value. So in essence unless you provide all of them, records will not even have a ‘max’ property and guess what happens when the templating engine tries to get a value for it :) But no worries, a solution is possible using yet another formula:

  1. column.Unbound("max").HeaderText("Max").DataType("string").Hidden(true).Formula("maxFix");

And in it you can assign values or in this case keep the ones available and place defaults. It doesn’t even have to be null-s anymore:

  1. function maxFix(row, grid) {
  2.     return row.max ?   row.max : "???";
  3. };

And you get quite lovely results:

igniteui-jquery-grid-unbound-columns-formulas-templates

Resources and demos

Donwload your Ignite UI Free Trial now!

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

TL;DR | Summary

In this article we covered the usage the Updating feature with Unbound Columns in the IgniteUI Grid – how to deserialize the now non-conforming model of data the client side jQuery widget is sending. We also looked into the proper methods of setting values so they can be persisted and how to deal with editing and formula updating upon dependency field change.

In addition to those, some light was shone upon how the Hierarchical version of the grid ever so slightly differs from the flat one and the unique challenges it poses – setting values and the dynamic creation of child widgets. The odd interaction between options such as Merge Unbound columns and Load on Demand and just how bad can things so and how useful the ASP.NET MVC Grid model ‘GetData()’ method can be to prevent that. Lastly, a small demo just to give head up when using Unbound Column values in templates without merging values on the server.

Get JavaScript code generated for you with the IgniteUI control configurator

$
0
0

 Configurator for IgniteUI jQuery controlsNot so long ago when Jason announced IgniteUI and a plethora of other goodness he mentioned a tool called Configurator that would help you code (or not code really) widgets included in the suite. As as he put it, for all of you server-side developers with desire to fire up your pages with some HTML5 and jQuery that sounds fantastic!

“Configurator for IgniteUI jQuery widgets?  Where?  Why is it not in my install folder/IDE?”

So yeah, there’s that. It’s not something you’d find in the installation of IgniteUI. It’s a live tool actually and there’s actually a link for that in the post in case you missed it.

You could always follow the direct link to the Configurator which is http://labs.infragistics.com/jquery/configure/, but it’s not all too confortable and far from enough don’t you think? Well we agree and we are moving forward with the Configurator making it easily accessible from the IgniteUI home page:

Location of the configurator tool on the IgniteUI homepage

There you go, now you know where you can find it when you feel like some fiddling.

Note: This is in a early stage (hence the big ‘BETA’ stamp on it). While we welcome you to give it a try, you shouldn’t expect correct behavior in all cases.

The Goal

Speaking of  fiddling the experience does remind me a bit of a reverse JFiddle – where you start from the results you want to see and get the code as a result. And that’s exactly what this tool is all about - it’s all “Developers, developers, developers!” deal with this one. And if you are like me and you love to learn by example and in our line of work that is by code. Then getting a ‘default settings’ code with very little knowledge of the IgniteUI controls – sure we want that. Also improve the learning curve of the controls? Why yes please! Experienced developers could instead use it instead to grab a quick piece of code to begin molding to their needs? Sure! There are a whole bunch of ideas behind such a tool that are just awesome. And you can say that yes we have samples (which I might add are quite awesome!) but they can’t cover all the events a control has as it would just not make sense in a real case. Also there are sometimes more properties that can be stuffed in there as well. And yes we do have rich API reference that lists all options and events, but hey what if you could see what they do instantly? What if the demo-ing control could also be modeled to your specification and data model? Well that’s the goal! To let you access and customize controls in a familiar fashion and with boosted speed and ease.

The Configuration ‘studio’

It’s kind of weird to have a getting started guide for a tool that is partly here to help with get star.. you get the point! Well ‘Introduction’ then. Firstly, you don’t really need to worry about resources and stuff, just follow the link and tweak/fiddle! Once there you will see the ‘studio’ has three main areas – toolbox with IgniteUI controls, live workspace and Visual Studio style properties and events tabs ( nice touch with familiar experience there):

IgniteUI configurator tool layout

Furthermore, on the matter of properties – the configurator is aware what are the default options for the controls so when met they will be omitted to produce clearer code. Let me give an example – as with the editor above you can set the vote count to 6 or 7 and that will reflect in the code, however when you enter back 5 the property won’t be in the source anymore as that is the default option to begin with. And once you are happy with the result you can hit “Source” and grab the corresponding code:

IgniteUI configurator generated jQuery / JavaScript code for the igRating

Note: While the code field is editable as I said this is like a reverse fiddle – the source is generated from the design rather than the other way around and changes made in code will just be lost once you switch to design. This is somewhat of a limitation regarding events as you can see – you get a handler function prepared for you, but since the tool can’t anticipate what it should do it is empty.

So we heard you like Grids…

So while the Configurator has a lot of catching up to do, but there has been a special focus on our powerful and feature rich jQuery Grids! And by feature rich in this case read “a plethora of settings to customize each” – I don’t even know if there is a golden middle between ease of use and freedom for the developer, I guess IgniteUI grid features being separate widgets helps some, but it does sometimes make it hard to find what you are looking for. For that reason the grids are sometimes difficult to configure and this is where this tool shines. Currently it does have some quirks still so I’ll attempt to guide you. Once you have picked a control you get it in a ‘sample’ version with some dummy data..that unfortunately isn’t being pre-populated in the options to the side. So if you want to get some customizations rolling, first make sure you have your data set. Simple walkthrough with a grid:

  • Start by setting the data source type and response key (if any, as with lack of scheme it is used to parse the data) before you paste actual data because for now the workspace is not equipped with a reset button and the grid will error out attempting to parse the data:

Data related options for the IgniteUI jQuery Grid

Also for advanced features it’s best to also set the primary key. After that you can paste your data in the data source field. Here’s mine and you can reuse or just provide your own:

  1. {
  2.     "Records" : [{
  3.             "BusinessEntityID" : 2,
  4.             "SickLeaveHours" : 20,
  5.             "VacationHours" : 1,
  6.             "test" : 1
  7.         }, {
  8.             "BusinessEntityID" : 3,
  9.             "SickLeaveHours" : 21,
  10.             "VacationHours" : 2,
  11.             "test" : 2
  12.         }, {
  13.             "BusinessEntityID" : 5,
  14.             "SickLeaveHours" : 22,
  15.             "VacationHours" : 5,
  16.             "test" : 3
  17.         }, {
  18.             "BusinessEntityID" : 6,
  19.             "SickLeaveHours" : 23,
  20.             "VacationHours" : 6,
  21.             "test" : 4
  22.         }, {
  23.             "BusinessEntityID" : 14,
  24.             "SickLeaveHours" : 21,
  25.             "VacationHours" : 3,
  26.             "test" : 5
  27.         }, {
  28.             "BusinessEntityID" : 15,
  29.             "SickLeaveHours" : 22,
  30.             "VacationHours" : 4,
  31.             "test" : 6
  32.         }
  33.     ]
  34. }

The grid is auto-generating columns  by default and once the source field loses focus the grid should appear.

  • Tweak options. Keep in mind field blurs cause the demo grid to reload with the new settings and produce errors if vital ones are missing/incorrect. Also enable and configure grid features! In the properties tab there’s a features option with an edit button that takes you to a view with all available features that can be enabled:

IgniteUI Grid features available for use with one click in the configurator

And as you can see each feature has its own ‘Edit’ button that takes you the respective feature widget options where you can toggle and set even more options and at any point you can hit the source and grab the generated code.

Note: What I explained for the grid with dummy data and the need to provide your own is true for all data bound controls at the moment. So, for example, a Pie Chart would require you to set data type, along with Value and Label member paths and paste something like the following JSON:

[{"Budget":60,"Label":"Administration"},{"Budget":40,"Label":"Sales"},{"Budget":60,"Label":"Finance"}]

This is the actual pie chart dummy data by the way :)

Just for now

For those experienced with web development the developer tools console of your favorite browser should feel like natural habitat. For those not so much experienced, well lets say that is while you are making changes something goes wrong for now the way to get at least some in for is by looking at the console. Also while at it you can use it to define global variables and functions to use with the controls – there are no iframe-s going on in the Configurator so you won’t to worry if those will be available.

Looking ahead

It seems this is quite often the case – the simpler a tool is supposed to make your like, the harder it would be for someone else to create it. It’s really a lot of work to make this perform flawlessly and then even more so to pack it up with amazing features. So as I mentioned before it is in beta stage and right now is the ideal moment for your voice to be heard.

For the future of the tool we’d like to let you add multiple controls to the workspace and also provide generation for ASP.NET MVC code. There are also a number of functionality enhancements and fixes and until then if you get stuck.. refresh away :)

We would appreciate feedback on what you think about the tool, what would like to see in the future and how would you like to use such a tool.

Donwload your Ignite UI Free Trial now!

As always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Reach further with IgniteUI jQuery Mobile controls and ASP.NET MVC

$
0
0

IgniteUI mobile applications with jQuery Mobile based controls and ASP.NET MVC

Out of the new and exciting things happening around IgniteUI is the enhancement of the mobile toolset. These controls are based on jQuery Mobile rather than jQuery UI, as it provides optimizations for mobile devices. Applications built using them meet the demands of phones and tablets when it comes to usability and design. Windows (Phone) 8,  iOS and Android devices are everywhere and the flavor of the year word is TOUCH  - support for that and responsive design both come out-of-the-box. But this isn’t the first time we’re taking on the subject – some time ago (in 12.1) we released the mobile ListView and Rating where some parts were covered. Even though for now I’ll keep to the present and show you some of the new stuff, leave a comment if you have some thoughts on anything else. Also make sure to check out the topmost link for a list of all IgniteUI changes.

So, possibly the biggest change introduced with 12.2 now is for all of you ASP.NET MVC lovers :) We’ve devoted effort to enrich the mobile toolset by adding 17 ‘new’ controls by providing what we call “re-assurance” wrappers around existing jQuery Mobile controls. What this means is that you get not only Infragistics support for them, but hey, as part of our suite you can bet on each of them having a model for neat ASP.NET MVC Helper action! Yup – means you can code jQuery Mobile based applications with Razor syntax in C# or VB.NET! Also you get enhancements such as the control model themselves so they can be configured outside the View as well for maximum control and when in the View – the option to create strongly-typed controls!

What are the goodies?

With the latest additions the IgniteUI package is capable of delivering an application in its entirety all in the comfort of the IDE and language you feel accustomed with. But it’s not just the controls that grew in numbers – to truly provide a full-fledged end-user experiences we also added themes for Android (whole 3 of them) and two for Windows Phone! And there’s even support for adaptive theming to match the device platform feel! I’ll try to demonstrate different themes along the way and you can always hit up our samples and see for yourself!

There are also a number of benefits you get – the mobile jQuery controls now have their own section in the IgniteUI Documentation/Online Help with very useful mobile-centric Deployment guide and detailed control documentation. You should totally check that one out as well.

In addition to what I mentioned above ( the comfort of your IDE and language, strongly-typed) the MVC wrappers bring quite a bit more – having server model of your client controls lets you define them without requiring the knowledge of their HTML structure and with IntelliSense support.

IgniteUI mobile MVC helper allows control creation in familiar manner and with the conforts of IntelliSense

They will also prefill defaults for you, the ones that matter the most – required for the control to be initialized,  automatic element IDs and in the case of strongly-typed controls like above the proper model property will be set as both ‘id’ and ‘name’ attribute of the elements in order to be properly picked up and sent upon form submits.

In case you think typed models of controls will take away the freedom you can have with just HTML, you can rest assured that is not the case as all the models accept extra settings via the HtmlAttributes method.

Getting started with mobile controls

Let’s see just how easy it is to create basic mobile applications. Make sure you have followed the Deployment guide above and referenced the jQuery mobile recourses  and IgniteUI ones or the mobile version of the loader widget.

The most basic control you could use is the TextBox that supports different modes such as password, multiline and search.

  1. @Html.InfragisticsMobile().TextBox().Mode(TextEditorTextMode.Search).Render()

And this in the Android “Holo Light-Dark” theme comes out as:

The jQuery Mobile search text box as produced by the IgniteUI MVC helper

Then you can have Buttons of course! Notable properties of the buttons are the icons (with related positioning properties) along with the ability to define them as inline (otherwise to ensure fit for small screens jQuery Mobile elements are usually displayed in block, as in a new line):

  1. @Html.InfragisticsMobile().Button().HtmlAttributes(newDictionary<string, object> { { "class", "pop" } }).Text("button").Mini(true).Icon("gear").Render()
  2. @Html.InfragisticsMobile().Button().IsSubmitButton(true).Text("submit").Inline(true).Render()
  3. @Html.InfragisticsMobile().Button().Text("cancel").Inline(true).Render()
  4. <style>
  5. .pop
  6. {
  7.     text-decoration: none;
  8.     line-height: 1.5em;
  9.     background-color: #ACACAC;
  10.     color: #fff;
  11.     border: none;
  12. }
  13.  
  14. .pop:hover
  15. {
  16.      background-color: #007FAF;
  17. }
  18. .pop:active
  19. {
  20.      background-color: #00AEEF;
  21. }
  22. </style>

I’ve also added the style in here so you can see that you can even make use of the html attributes to add class and apply custom styling:

jQuery Mobile buttons with custom styling and icon and two inline buttons

That doesn’t really fit, but oh well :)

Next up is the Slider so let’s see how you can set it up on the server using the model:

  1. using Infragistics.Web.Mvc.Mobile;
  1. SliderModel slider = newSliderModel();
  2. slider.NumericInputDisplayMode = DisplayMode.Inline;
  3. slider.MinValue = 0;
  4. slider.MaxValue = 100;
  5. slider.Value = 30;
  6. slider.Label = "Progress:";
  7. slider.LabelAlignment = Alignment.Top;
  8. ViewBag.Slider = slider;

You can pass the model itself if you will, but I’m actually using the dynamic ViewBag because I have more then one model for that view. The Helper as always comes with an overload taking a ready control model:

  1. @Html.InfragisticsMobile().Slider(ViewBag.Slider asSliderModel)

Mobile slider in the IgniteUI iOS theme

Moving on with the all-time favorite Toggle switch. It’s one of those mobile specific controls that is very easily recognized. The two states of the control are called on and off and you can specify text for both, as well as initial state. With all controls you can also apply themes (or swatches as they are defined within the jQuery Mobile styling):

  1. @( Html.InfragisticsMobile().ToggleSwitch().Render()
  2. )
  3. @( Html.InfragisticsMobile().ToggleSwitch().ID("test2").OffText("disabled").OnText("enabled")
  4.         .SwitchedState(true).Width("10em").Theme("b").Render()
  5. )

Mobile toggle switches in the IgniteUI Android theme and tow different swatches (sub-themes)

Those are the Android-styled toggles.

Another very mobile-oriented control would be the Select Menu– very much behaving like a select but with enhanced popup-like modal selection for better mobile experience. Much like other selects and combo boxes, it supports both single and multiple selection, the option to use native menus ( don’t know why would you punish users like that, but it’s there) and a collection of items:

  1. @Html.InfragisticsMobile().SelectMenu().Multiple(true).Label("Favorite language:").NativeMenu(false).Items(s =>
  2.     {
  3.         s.MenuItems.Add(newSelectMenuItem { Text = "JavaScript", Selected = true});
  4.         s.MenuItems.Add(newSelectMenuItem {  Text = "C#", Value = "CSharp" });
  5.         s.MenuItems.Add(newSelectMenuItem { Text = "TypeScript"});
  6.         s.MenuItems.Add(newSelectMenuItem {  Text = "VB.NET" });
  7.     }).Render()

The initial state of the IgniteUI select menu with a label and default selection

The interesting part is that it would also display a counter if multiple are selected(if you want it to naturally – as with most it is customizable):

The popup with options shown for the Select menu and the dynamic counter for selected items.

Speaking of selection there are two more well-known user-selection that the above menu is really based on. First are the Checkbox and Checkbox Group– fairly simple two-state input element, that when used in group allows for multiple selection. The checkbox can be used as stand-alone as expected or as part of the group model’s collection of items:

  1. CheckBoxGroupModel cbgrp = newCheckBoxGroupModel();
  2. cbgrp.ID = "chkBoxGrp";
  3. cbgrp.Items.Add(newCheckBoxModel() { Checked = true, ID = "opt1", Text = "Option1"});
  4. cbgrp.Items.Add(newCheckBoxModel() { ID = "opt2", Text = "Option2"});
  5. cbgrp.Items.Add(newCheckBoxModel() { ID = "opt3", Text = "Option3"});
  6. ViewBag.ChbxGrp = cbgrp;

  1. @Html.InfragisticsMobile().CheckBoxGroup(ViewBag.ChbxGrp asCheckBoxGroupModel)

A checkbox group in Windows phone theme (light)

And if you set the ‘Horizontal’ property to true you get a single line button or also called toggle set:

A horizontal version of the checkbox group in Windows phone theme (light)

The other selection option in the toolset is the Radio Button Group– with the difference that radio buttons make no sense outside of a group and when grouped only a single one can be selected. Again as you’ve seen above you can set initial selection, but the group model itself can be assigned a ‘SelectionIndex’ (but keep in mind it is overridden by selection assigned on the radio elements themselves). Also, much like above the the group can be set up as horizontal button set:

  1. @(
  2. Html.InfragisticsMobile()
  3.     .RadioButtonGroup()
  4.     .ID("ageRange")
  5.     .SelectedIndex(1)
  6.     .Items(item =>
  7.     {
  8.         item.RadioButton().ID("grp1").Text("18-25");
  9.         item.RadioButton().ID("grp2").Text("26-35");
  10.         item.RadioButton().ID("grp3").Text("36-45");
  11.         item.RadioButton().ID("grp4").Text("45+");
  12.     })
  13.     .Render()
  14. )

A radio button group in Windows phone theme (light)

A horizontal version of the radio button group in iOS theme

As form elements

Since in this article the main focus is on input elements, I feel it’s important to explain a bit how they work in the context of HTML forms. Usually you will be working with all familiar elements, however as mentioned above some controls provide the ability to use native ones – what does it mean? Well, even though there are probably default UI elements available, in jQuery mobile standard elements are primarily built out of spans. This ensures no browser enforces its own default UI or behavior and therefore cross-browser compatibility. This applies to things such as buttons, checkboxes, radio elements and so on. However, in the interest of remaining with a functional application when jQuery mobile is not supported (simple HTML mode, no JavaScript - although I have no idea where you'd find that except for those that turn it off on purpose), buttons (and other inputs) intended to perform the default browser submit functionality will be created as styled standard input of type submit.

Also a very important part is that the IgniteUI ASP.NET MVC wrappers will create the proper markup (such as intended to be used with jQuery mobile) that would create native browser elements and assign them the proper ‘id’ or ‘name’. Those contain the actual values and are the ones picked by the browser on submits. However, when jQuery mobile is functional they will remain hidden, while the user interacts with the enhanced mobile controls. That also means in case something goes wrong the application will have this whole set of active controls and will perform as expected, but with reduced usability. Again this is something you won’t have to worry about, but it’s good to know.

TL;DR | Summary

We’ve looked into mobile controls that assist with user input and help you create immersive mobile experiences that implement cross-browser and cross-platform responsive design and go beyond – while optimized for touch and mobile, jQuery mobile applications actually look and work beautifully on desktop browsers! There’s a very extensive list of supported platforms and let’s just say the A-grade list is impressive.

But there’s more – the IgniteUI mobile toolset will also provide you with a number of wrappers around layout elements to help you build your application that much easier in a familiar way and environment. So stay tuned for the upcoming blog demonstrating those, demos and more! In the meantime you can always try it for yourself:

Donwload your Ignite UI Free Trial now!

And as always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Ignite UI jQuery Mobile layouts and ASP.NET MVC– wrapping it all up

$
0
0

Ignite UI mobile applications with jQuery Mobile based controls and ASP.NET MVCAfter we’ve looked into the range of input jQuery mobile controls you can use with the greatest ease with Ignite UI ASP.NET MVC wrappers, there is something more you can do. If you try the snippets as is you will find them working well and in tone, but rather lacking in terms of layout. So now that you have an idea how to provide content, let’s wrap it up in some proper containers and provide navigation.

For the record, it’s only fair to say that if you run a sample with just the controls defined you still get the entire page styled. Why is that? Well you can have a look at jQuery Mobile’s “Anatomy of a page”. As it says there the layout consists of conventions, rather than requirements. Wheat this means is that you are not forced to add a header, footer, content or page for that matter. What jQuery Mobile will do is to wrap your entire HTML in the body in a DIV with the page role. That while convenient is not optimal – if you have script tags in the body the process of wrapping the content will also load them again, so it is best advised to still have a properly defined mobile ‘page’ in your HTML. This is also absolutely necessary when two or mode pages are to be delivered in a single file, and yes, that is possible, but again not recommended way of design. So yeah, conventions and not requirements, however those conventions are there for a good reason and you are better off following them. So how about Ignite UI ASP.NET MVC wrappers? Will they help here? Sure!

Mobile pages 101

Let’s start with the most basic thing – the actual page. The helpers will  offer a way of defining it that is very similar to how you would go about a Form element. That is in the sense that there are separate methods to begin a page and to end it. However, unlike the form being a set of two methods, the page being Ignite UI means it can also be initialized with a Page Model passed to the view. Here’s how a simple definition with chaining syntax goes ( logically, any options should be in the page opening) :

  1. @using Infragistics.Web.Mvc.Mobile
  1. @Html.InfragisticsMobile().Page().Title("My App").Theme("b").BeginRender()
  2. <!-- page content goes in here -->
  3. @Html.InfragisticsMobile().Page().EndRender()

And as I mentioned this can be done via Page Model:

  1. PageModel page = newPageModel();
  2. page.Title = "My App";
  3. page.AddBackButton = true;
  4. page.BackButtonText = "Lost? Go back!";
  5. ViewBag.secPage = page;
  1. @Html.InfragisticsMobile().Page(ViewBag.secPage asPageModel).BeginRender()
  2. <!-- ... -->
  3. @Html.InfragisticsMobile().Page().EndRender()

How about that back button setting up there? What could that possibly do? Well it’s related to the next otherwise not required layout element – the header. For that setting to have any meaning, you will need a header on your pages as the button will appear just there). There’s another button that you can set text (for tooltip) to but you can’t customize, and that is the close button. Page with close button? Of course, that one is present when the page is opened as a dialog. We’ll talk about how that goes later or, but keep in mind it only makes sense to set one as the close is dialogs’ replacement for the back button.

Headers are used in much the same way as pages (begin and end) and among other things can be set to fixed position:

  1. @Html.InfragisticsMobile().Page(ViewBag.secPage asPageModel).Url("secondary").BeginRender()
  2.     @Html.InfragisticsMobile().PageHeader().FixedOptions(options => {
  3.             options.Fixed(true).TapToggle(true).Transition("slide");
  4.     }).BeginRender()
  5.         <h1> Second page</h1>
  6.     @Html.InfragisticsMobile().PageHeader().EndRender()
  7.     <!-- ... -->
  8. @Html.InfragisticsMobile().Page().EndRender()

The ‘FixedOptions’ can also set behaviors like tap (or click) to show/hide the fixed header and transition animation for showing (the default is slide). There’s more of course and you can pay our Ignite UI Mobile Documentation a visit. The And you will see something similar to this in the light WP theme:

Ignite UI Mobile page's header in Windows Phone Light theme

Linking

One thing still is escaping – how did the user get here? Well he followed a link of course. jQuery mobile provides some enhancements to those as well. Links can be in three styling modes – the native for the browser, the link styling of the theme or as a button. Note that links inside headers and footers will always be styled as buttons whether you set them to or not. Also I mentioned dialog and you can tell the framework to load something as dialog when you specify an option on the link itself:

  1. @Html.InfragisticsMobile().Link().NavigateUrl("#secondary").RenderMode(LinkRenderMode.Button).Text("secondary page").Render()
  2. @Html.InfragisticsMobile().Link().NavigateUrl("#dialog").DestinationRelationship(LinkDestinationRelationshipOptions.Dialog).Text("dialog page").Render()

The above will produce:

Ignite UI Mobile links - with dialog relation and styled as a button

As you can see you have the option to define a relation with the destination or the link, and this is how you create interactions to open up dialogs and popups. This is a similar page opened up by the dialog link above:

Ignite UI Mobile page opened as a dialog

You can also specify relation to be ‘back’ so you can add back buttons not just in headers but anywhere in your content. Keep in mind automatic Back button on on pages only appears if there's actual history – as in when they have been navigated to by a link. This is somewhat important to know when using multiple pages in a single document (even thought it’s not a recommended practice) as you can force loading secondary pages by entering their URL but this way jQuery Mobile won’t add those to the history. Still, there’s a very minor chance someone will be entering links by hand, but you never know. Also for back buttons it’s good to still provide a meaningful link for browser compatibility.

More?

So far you can create basic pages and provide navigation. Also at your disposal is the content element – as the documentation describes it is helpful for “providing some basic formatting and structure”. But, hey, have you noticed the simulator screenshot in the previous blog? Does it seem odd items are stretched so far out it feels they are about to be cut off by the viewport edges? That’s because as direct page content they tend to spread to the maximum width… Wrapping those in a content element is at least the easiest way to provide proper formatting as the content will squeeze them a little. Also you can set separate theme to he content this way or apply classes to add custom styling and behavior with.

The same statement that describes the content is also true for the footer. But while it is not required for automatic page functionality like back/close buttons and it also serves the structural and potentially visual separation of content, it can much like the header be fixed, which can sometimes be really useful. So for that secondary page we have above we can add something like the following:

  1. @Html.InfragisticsMobile().PageContent().BeginRender()
  2.     There's some content in here. x 50
  3. @Html.InfragisticsMobile().PageContent().EndRender()
  4.  
  5. @Html.InfragisticsMobile().PageFooter().BeginRender()
  6.     <p> (c) My Company </p>
  7. @Html.InfragisticsMobile().PageFooter().EndRender()

Ignite Ui mobile page with a header and footer

Enhanced Layout

Now that you can properly fill those pages comes yet another potential problem. While you have been refactoring your site’s experience into mobile pages to be properly consumed on mobile devices it still is too hard to fit relevant content together into a single page? Well you don’t have to, really.

You can separate content that is not for immediate consumption or instead of swapping pages on some event show content in a popup. It’s extremely useful for a range of cases – from simple tooltip to login forms and application menus. Keep in mind the popup is available from 1.2.0 version of jQuery Mobile! Like other content containers, the popup has begin and end methods and content between them is not displayed by default. To display a popup you can set up a link (on the same page) that navigates to the popup ID and has a relation of that type and inside you can add another link as close button:

  1. @Html.InfragisticsMobile().Link().NavigateUrl("#DownloadPopup").DestinationRelationship(LinkDestinationRelationshipOptions.Popup).Text("Click here!").Render()
  2.  @Html.InfragisticsMobile().Popup().ID("DownloadPopup").History(false).PositionTo("window").Tolerance(10).Transition("pop").BeginRender()
  3.      @(Html.InfragisticsMobile().Link().NavigateUrl("#").DestinationRelationship(LinkDestinationRelationshipOptions.Back).RenderMode(LinkRenderMode.Button)
  4.              .Icon(DefaultIcons.Delete).IconPosition(IconPositions.NoText)
  5.              .HtmlAttributes(newDictionary<string, object>() { {"class", "ui-btn-right"} }).Render())
  6.      <atitle="Donwload your Ignite UI Free Trial now!"href="http://www.infragistics.com/products/jquery/downloads"target="_blank">
  7.          <imgsrc="@Url.Content("~/Content/IgniteUI-download.jpg")"alt="Download Ingine UI"/>
  8.      </a>
  9.  @Html.InfragisticsMobile().Popup().EndRender()

Ignite UI mobile popup

The button class can be left and right and the contents of the popup can be any HTML. As seen options include positional settings like centering over the element that was clicked (origin) or the window. You can even pass a jQuery selector to position the popup over the matched element. And the tolerance sets up how much in the minimum spacing between the popup and the edge of the window. The MVC wrapper method has 3 overloads that let you put a single common value, just for top and left or for all sides. The transition has the usual jQuery animations available and another allows (or not) for jQuery Mobile to modify the page Url and add it to the history. This means you can let your users to close popups with the back button.

Another extremely useful layout trick is to organize things within collapsible container. They come in two varieties – a standalone  Collapsible and a Collapsible Set. While pretty self-explanatory the single version consists of a header and a content block that is either visible or collapsed. You have a number of options to control themes, header state icons and their position and more. For example the default icons are plus and minus, while we can use arrows instead:

  1. @(Html.InfragisticsMobile().Collapsible().HeaderText("radio button groups").ContentTheme("c")
  2.         .ExpandedIcon(DefaultIcons.UpArrow).CollapsedIcon(DefaultIcons.DownArrow).BeginRender())
  3.         <!-- ... -->
  4. @Html.InfragisticsMobile().Collapsible().EndRender()

Ignite UI mobile collapsible content in both states and with modified state icons

The set version is essentially an accordion – only one out of a group of blocks can be expanded at a time and current collapses automatically when the user opens another. The model is pretty much identical – you have the set that is a container for single collapsibles and it even has the same general options (icons, themes) that you can apply for all child blocks:

  1. @Html.InfragisticsMobile().CollapsibleSet().ContentTheme("c").BeginRender()
  2. <!-- standalone collapsibles here -->
  3. @Html.InfragisticsMobile().CollapsibleSet().EndRender()

Ignite UI mobile collapsible set of content with second block expanded

A nicely packed navigation

Of course, it’s always nice when you can make the web page feel more like an application and provide nice buttons instead of links. It’s also nice if those are not spread all over the page, but rather grouped. Here is where the Navigation bar comes in play – it allows you provide up to 5 (after which it wraps them in multiline..) buttons with customizable themes, icons and exclusive selection (kinda like horizontal radio group but without the form input support). This toolbar is most common in headers or footers. The control itself has a collection of items, global icon positioning an theme options. Each item has Url and text as expected:

  1. @Html.InfragisticsMobile().NavBar().IconPosition(IconPositions.Top).Items( item =>
  2.     {
  3.         item.NavBarItem().NavigateUrl("#secondary").Text("secondary page").Icon(DefaultIcons.RightArrow);
  4.         item.NavBarItem().NavigateUrl("#dialog").Text("dialog page").Icon(DefaultIcons.Info);
  5.         item.NavBarItem().NavigateUrl(Url.Action("Index", "Type")).Icon(DefaultIcons.Gear).Text("Strongly-typed");
  6.     }).Render()

Ignite UI mobile page's footer with a NavBar with 3 buttons (focus on first one)

TL;DR | Summary

In this article we’ve looked into the basic ways to create structure in your mobile applications using the Ignite UI ASP.NET MVC wrappers/helpers. In addition, we saw how linking between pages can be done with different relations and styles. Furthermore, there were the mode advanced ways to enhance experience with your app by providing interactive content with popups and collapsible content. Lastly, a more neatly organized way to build your navigation in a toolbar that also provides a nice touch of ‘native’ feel to the mobile app.

Make sure you stop by the first part for some input jQuery mobile controls you can use with the greatest ease with Ignite UI ASP.NET MVC wrappers, check out our Ignite UI Mobile Documentation and visit our samples you can experience on your mobile device as well! I have packed all of the described examples in a very simple ASP.NET MVC solution for you to download and try and in order to run that you will need at least a Trial version of Ignite UI for the mobile assembly, but hey it’s free, grab one from here:

Donwload your Ignite UI Free Trial now!

And as always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

The Future of Story Tests with Selenium WebDrivers and ISTA 2012

$
0
0

Test Automation: A Future with Selenium WebDrivers, ISTA 2012 conference presentationThis year we had the pleasure of being part of the second “Innovations in Software Test Automation (ISTA 2012)” conference! This year with even more companies joining for knowledge sharing, additional workshop/hands-on track and a lot more participants. ISTA is gathering a healthy range of quality engineers, software test automation experts and developers to the common goal of sharing new technologies and best practices in the fields of automation. This conference provides for a greater exposure of the software test automation role, which makes it a good place for some to choose career path, gain new insight( or share some of their own! ) and generally gain a better  understanding of just how diverse problems can be met and solved in the field. Our Principal Architect, Angel Todorov, who’s been recently focused on our Ignite UI jQuery controls gave an awesome presentation on Automated Web UI Testing… on Steroids! More on that later. The goodness spreading doesn’t have to be only once a year, right? Right. So the conference is also issuing a quarterly newsletter (you can sign up here). One of our talented Quality Engineers, Borislav Traikov,  has been providing some food for thought via article for the conference newsletter.

The Future of Story Tests with Selenium and WebDrivers

Selenium has been a recurring mention when it comes to Web Test automation – either directly, or a subtle-yet-preferred base for a framework, it comes up one way or another. And frankly I think that’s a good thing, as major companies having interest in the project simply means better support and contribution to it. And I’m definitely not the only one – taken from Borislav’s article:

Performing automated tests on web applications can be crucial to the success of a project, due to the fact that cross-browser support is a mine field for potential problems and defects. On the other hand, web applications are platform-independent (for the most part). Thus, they can be tested natively (with frameworks such as QUnit) in the browser, or by frameworks that utilize first-class languages (such as C#, Java, Python and so on, while this choice provides a wide window for automated testers to step in and start writing tests. So, if a web application testing framework is to be successful, it should satisfy these needs. Selenium has been doing great so far, providing a cross-browser API over multiple languages. While the Selenium API provided great DOM abstraction and traversal, it had its shortcomings – the biggest of which was its use of JavaScript sandboxing (Selenium v.1, also known as Selenium RemoteControl, used the JavaScript engine of the current browser as a way of interacting with it). In most cases that wouldn’t matter, but with more complicated web applications under test, interaction with JavaScript events, rather than emulation of real mouse clicks or keyboard interaction simply wasn’t enough. Also, the Selenium API was growing steadily with each release, and while that was awesome (grey areas were addressed and bug fixes were applied), one had to wait for a release or two to get the API method that they sorely needed (or extend the framework themselves – after all, Selenium is an open-source framework).

While Selenium was rising to fame among automated testers and gathering commercial support, an engineer at Google wanted something more: a test framework that would break free from the JS sandboxing and instead would natively speak to the browser. With so many different browsers, that was no small feat. Thus, the WebDriver project was born. With backing from Google, this automatically meant that Google Chrome (well-known for its distribution of stable nightly builds) and its little brother: the Android browser would be supported. This alone would open the door to a huge improvement in test automation, seeing as how the Android browser holds a solid 24% of the mobile OS market (during the time of writing of this article). However, the other runners-up in this “mobile browser war” – Safari and Opera – are still tough challenges for any cross-browser testing framework.

In 2008, the Selenium and WebDriver teams decided that it was time to join forces and to improve Selenium with the advantages of WebDriver: Selenium WebDriver. The transition from RemoteControl to WebDriver is still going strong alongside the continuous development of the WebDriver API – these facts raise both hopes and questions about the near and far future of test automation with Selenium.

There’s a ton of interesting info in there, pros and cons of the WebDriver and some thoughts on the (hopefully) bright future of test automation. It’s definitely worth checking out, so go ahead and read The Future of Story Tests with Selenium and WebDrivers!

Honorable mentions

That’s not all! There are more topics to spark your interest:

Software Localization … more than just using Google Translate, by Desislava Nikolova

Best Practices: How To Test Performance Of A New Product, by Grigor Svetoslavov

Automated Web UI Testing on Steroids:

Automated Web UI Testing on Steroids: Behavior-Driven Development, Headless Browser Environments, and Test Bed Generation.Behavior-Driven Development, Headless Browser Environments, and Test Bed Generation.

As I mentioned Angel Todorov presented some of the methods and innovations used right here at Infragistics. Yup, it’s basically info straight from source and it’s pretty interesting stuff as well. The presentation covers a number of limitations, problems and approaches to address them. It starts by giving an overview of some common frameworks and practices used to automate Web UIs, and lists their advantages and disadvantages. It continues by describing a unified framework for UI automation, involving a combination of tools, some of which come from relatively new concepts such as headless browser environments. The key goals? -  Faster test execution, more reliable test results, improving code reuse, and achieving greater productivity!

There are details on the cucumber-based framework we are using to describe scenarios and features in the Gherkin syntax and the tools to integrate that into the process. It’s a style that provides readability for the test cases so the stakeholders and managers can take part of the action. Again, something you should definitely not miss – you can follow the link above to read some more and there’s a downloadable version of the presentation deck. For those of you that can’t wait here’s a direct link to slides for the Automated Web UI Testing on Steroids presentation.

 

I’ll end this with something I’d like to say, but as you see I’ll have to just repeat:

@ISTA_Conference

That was ISTA 2012! You guys rock, thank you! pic.twitter.com/JGnqlToy

Embedded image permalink

And see you next year!

Ignite UI Tree Drag & Drop

$
0
0

Ignite UI Tree control with Drag and Drop funtionality based on jQuery UI interactionsWith the latest release the Ignite UI Tree has received the nifty feature of drag and drop. It’s a fairly common concept and it allows for a natural (at least I perceive it to be for a long time) interaction with the control’s items. From a user’s stand point it’s all but awesome news  - this would allow for nodes to be moved between one tree’s structure and in our case there’s a bonus of being able to do that between two trees even. The feature provides rich interactions with a familiar visual touch out-of-the box.

From a developer’s view things are also quite pleasing – the jQuery Tree Drag and Drop already handles a decent range of common case scenarios, so you can almost set one property and umm.. profit! Then again, when you do need to set things up it offers a possibly familiar API as you’ll see and has pretty much everything conveniently exposed. And while the widget itself will perform a solid range of drop validations it offers you the option to define your additional custom function.

Quick Setup

As advertised, this can be achieved via a single property and you can then kick back and enjoy the result with the defaults. Drag&Drop is build directly into the tree, so no additional files required and main star is the ‘dragAndDrop’ property:

  1. $('#tree').igTree({
  2.     dragAndDrop: true,
  3.     dataSource: northwindCategories,
  4.     bindings: {
  5.         textKey: 'CategoryName',
  6.         primaryKey: 'CategoryID',
  7.         valueKey: 'CategoryID',
  8.         childDataProperty: 'Products',
  9.         bindings: {
  10.             textKey: 'ProductName',
  11.             valueKey: 'ProductID',
  12.             primaryKey: 'ProductID'
  13.         }
  14.     }
  15. });

This setup will pretty much produce a fully functional tree with the bare minimum of setup and the equivalent ASP.NET MVC helper definition is about as simple:

  1. @(Html.Infragistics().Tree(Model).ID("tree")
  2.     .DragAndDrop(true)
  3.     .Bindings(binding =>
  4.     {
  5.         binding.PrimaryKey("CategoryID")
  6.             .TextKey("CategoryName")
  7.             .ValueKey("CategoryID")
  8.             .ChildDataProperty("Products")
  9.             .Bindings(b1 =>
  10.             {
  11.                 b1.TextKey("ProductName")
  12.                 .ValueKey("ProductID")
  13.                 .PrimaryKey("ProductID");
  14.             });
  15.     }).DataBind().Render())

The resulting tree is the one you see in the heading image. Yes, the helpful indicator to the side too! Make sure you stop by the documentation and check out the Enabling Drag-and-Drop section where you can discover that there’s an additional ‘allowDrop’ setting that allows for nodes to be dropped in you igTree from another igTree!

Customizations

Now we come to the part that may be familiar to some of you – the Ignite UI Tree also accepts a ‘Drag and Drop Settings’ object with various settings for the behavior of the feature. Now a small deviation  - the reason why the settings might be not so hard to understand is because the control internally implements interactions from jQuery UI– the Draggabe (“Allow elements to be moved using the mouse.”) and Droppable (“Create targets for draggable elements.”) widgets. Taking a look at the descriptions for those you can tell why – just like the concepts of Drag and drop go together, so do those widgets and the tree  initializes both itself. Oh and that settings object? Yeah, some settings go almost directly to the Draggabe Widget’s options:

  • helper – Defines what element (helper) should be used to for dragging display. More on that in the future, lets say you can use the original node or a clone too, just don’t deep clone :) Plus the helper I think will almost always end up being the better UX.
  • dragOpacity( equivalent to ‘opacity’) – Opacity for the helper while being dragged.
  • zIndex – This directly corresponds to the CSS z-index Property that will be applied to the dragged element. It specifies the stacking order of elements – making the ones with higher index come on top of those with lower. The default 10 is usually enough, but if for some reason you are having issues with your dragged helper being overlapped by something this is the property to increase.
  • revert – this one is mildly different as for the Tree it’s a Boolean while the jQuery UI Draggable takes both boolean and two string options. The reasoning behind this is that in the context of drag and drop within the jQuery tree not all of those make sense – like reverting on ‘valid’ drops or always reverting. Plus it makes the option much simpler – false stays the same, while true defaults to ‘invalid’ instead.
  • revertDuration - The duration of the revert animation, in milliseconds. Ignored if the revert option is false.
  • dragStartDelay ( equivalent to ‘delay’) - Time in milliseconds after ‘mousedown’ until dragging should start. This option can be used to prevent unwanted drags when clicking on an element. This one is big on user experience I think – trust me when I say you don’t want this too low with complex control with elements that perform additional functions. Based on that the minimum you want is at least 100 milliseconds or you risk drags starting on simple selection and node expansion. It’s pointless overhead and even potentially successful but unintended buy the user moving of nodes.
  • containment - Constrains dragging to within the bounds of the specified element or region. This one is quite versatile – taking a range of values like a selector, the actual element to contain within, string enumeration or even an array of values defining a bounding box. The default false setting restricts the drag to the parent element of the tree.

Possibly half of the others are left to the default and some the Tree widget reserves for itself. There are of course ways to ‘hack’ into those, but let’s not do that.. at least not just yet. But hey, here’s one that barely counts as one – the cursorAt that defines where the helper markup should appear relative to the mouse cursor regardless of what the name may imply is also defined, however it’s value is set form a constant within the tree and that can be replaced. Keep in mind there’s probably a good reason for that not be exposed, so use with caution – there’s at least one danger  - if you go too far and bring the helper under the mouse (which you might decide to do) I guess the validation of the drop target fails and all you’ll be seeing is invalid visual – you need the mouse at least 1px clear from the helper sadly. But hey, you can at least reposition the helper closer and to the bottom right corner (Explorer style) for example:

Ignite UI Tree control with Drag and Drop helper position and markup manipulation

I’ll admit I didn’t do a particularly good job with that, but hey it’s good to know you can tweak that. If you have been playing with the feature so far you’ll also notice the invalid message is different (it usually  shows the name of the node you are dragging) – that’s because you can provide markup for any of the multiple helper states. You can look those up in the Drag-and-Drop Property API Reference where you’ll see the defaults too. Oh right, the code:

  1. $.ig.loader('igTree', function () {
  2.     $.extend($.ui.igTree.prototype, {
  3.         _const: {
  4.             dragCursorAt: {
  5.                 top: -20,
  6.                 left: 0
  7.             }
  8.         }
  9.     });
  10.     $('#tree').igTree({
  11.         dragAndDrop: true,
  12.         dragAndDropSettings: {
  13.             dragAndDropMode: 'default',
  14.             dragStartDelay: 100,
  15.             revert: true,
  16.             invalidMoveToMarkup: '<div><p><span></span> <strong> Invalid location. </strong></p></div>',
  17.             containment: "#tree"
  18.         },
  19.         dataSource: northwindCategories,
  20.         bindings: {
  21.             textKey: 'CategoryName',
  22.             primaryKey: 'CategoryID',
  23.             valueKey: 'CategoryID',
  24.             childDataProperty: 'Products',
  25.             bindings: {
  26.                 textKey: 'ProductName',
  27.                 valueKey: 'ProductID',
  28.                 primaryKey: 'ProductID'
  29.             }
  30.         }
  31.     });
  32. });

Regarding the various markups – you’ll notice they all contain an empty span. What’s up with that? Well, the span is the one being targeted by the widget to apply classes for the icons. If you want the icon it’s good to include a span in your markup. You can remove it to remove the icon, or leave just the span (with unbreakable space in the paragraph for height) to have a helper with icon only.

Notable mentions from the snippet above also include the mode of the feature ( move and copy available, the default option combines both with a ‘Ctrl’ as modifier. The line at 17 (containment: "#tree") prevents the dragging to go on beyond the boundaries of the tree itself (as in my case with a very simple layout the tree’s parent is the body and that doesn’t always produce good results).

Enhanced Events

The Tree will fire a number of events during the drag and drop interaction. The familiar equivalents of jQuery UI Draggable (start –> drag –> stop) are as follows: ‘dragStart’ –> ‘drag’ –> ‘dragStop’. A slight name change really to better suit the context of a more complex widget. Speaking of which, the control as you can imagine internally handles the original Draggable events to do its work and conveniently fires event for you to handle in the meantime, but that’s not the whole story. While, yes, the Tree will pass you the original Event object, the arguments you get alongside will be extended with a bunch of useful properties containing the path of the node in question, the actual element itself, its data record and appropriate binding. Again you can refer to the API reference or Documentation to get additional info on those. All are cancelable.

Since the Ignite UI Tree also must be the target of the drop, it also has an initialized Droppable widget. However, the events for that are different – the tree will fire events as the node is being dropped and after it has been dropped. As you can imagine the first one can be canceled and is a good place for last checks to see if the node dropping is in an acceptable position. These come from the single ‘drop’ event of the jQuery UI widget, so what happens with the rest? Well, they didn’t make much sense for the Tree, but if you really need to handle some of them for any reason don’t despair – unlike the draggable deeper inside the structure of the control, the Droppable is initialized of the very same element as the igTree and you can handle it’s events just as described in it’s API, for example:

  1. $("#tree").on("dropactivate", function (event, ui) {
  2.     alert("We have take off!")
  3. });

Also you can handle the ‘drop’ as well, but keep in mind you won’t get the extra info the Tree provides in the delegated events and when you do handle drop your handler is likely to be triggered before the Tree’s so you can’t really verify the job is being done there. Just, whatever you do, don’t handle those with alert like I did! :)

Limitations and not so much

I mentioned the control will do a plentiful amount of validation (even without a custom function) in order to maintain reasonable behavior (like the droppable not accepting anything you throw at it an such). The tree will also validate the drop target is an acceptable part of an igTree widget’s UI. in addition to that, as I mentioned before, there is also a binding validation. What this means is that the node being dropped on must have bindings compatible with the dragged node. Take the case above – we have a top tier of Categories whose children are products. Based on that the control will not allow you to drop one Category into another (as the accepted child is defined as Product already). Same goes with trying to drop product in the root (where category is expected). If your models allows identical bindings on all levels then you can ignore that.

This leads to some issues with performing Drag & Drop between two trees as well – you can’t simple define two completely unrelated trees and expect to be able to move nodes between them. Again similar bindings should exist in the trees in order for the move or copy to be accepted. I’m not sure if it’s a limitation if it’s reasonable, but hey you can always work some magic to get around it.

Resources

First let me again remind of the elaborate Ignite UI Tree Documentation and jQuery API Reference. Then don’t forget the awesome samples:

  • Drag and Drop within a single Tree– you can observe here two interesting behaviors of the control – when checkboxes are enables their state will not be preserved when moving, however when dropped on a parent whose tri-state box is fully checked (meaning every child also is) then the newly added node will follow that rule and also render with ticked checkbox. Also dragging a node over unexpanded one will (after a short timeout) expand the drop target.

  • Drag and Drop with two Trees–  moving nodes between controls with identical binding while avoiding dropping nodes on the ones marked as a file via a custom validation function.
  • Simple File Manager– Using Drag & Drop to organize files and folders and an additional droppable target you can drop nodes to delete them – all while watching the respective events fly in the log.

Stay tuned!  There are some more neat tricks to share about this feature and I plan on another part along with demos!

Summary

We can all agree that Drag and Drop is awesome, simple and intuitive and it’s always great to be able to provide that for your users. As we’ve seen that is as easy as child's play with a mere property or two. However, once you know what you want and the defaults are not it – there’s a plethora of knobs to tweak and some of them.. do they sound familiar? Yes they do, as some come directly from the jQuery UI Dragable and Droppable widgets. Helpers are separated for cases so each can be controlled separately. The control handles much of the work for you by validating proper drop targets on multiple criteria while still providing you with the option to build on that – be it with validation function or by hooking up to one of the elaborate events. Go check out those samples and stay tuned for more!

Donwload your Ignite UI Free Trial now!

And as always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Tips on Drag & Drop with the Ignite UI Tree

$
0
0

Tips on Drag & Drop with the Ignite UI TreeAs promised in the previous blog on Drag & Drop with the Ignite UI Tree– some tips and tricks coming right up. If you haven’t already looked into the control’s features, now’s your chance – overview with main ones listed in the Documentation and live in the Samples. Looking at those, while each of them is rather nice, how do you think some of them would work alongside each other…and is something missing? The first one is always a potential trouble maker and the latter.. well extending functionality is fun, but as you know there are always new stuff coming with each release – might be just the thing you are missing. We do take feedback on everything through socials (links always below), there’s an active community at the Forums and you can always submit a feature request( must be logged in). Enough with the shameless plugs, on to the discussion!

Play nice?

As you’ve already seen in my previous post there are some quirks for example when you have nodes moving around with Drag and Drop and checkboxes at the same time you will loose the checkbox state and when a tri-state parent is fully checked a dropped node will respect the valid-for-all rule.

It’s merely the fact that while drag and drop is a nice interaction from a user standpoint, underneath it involves removing and adding nodes. While that is simple in itself, it does present a number of situations where it can be really tricky to preserve whatever state the node may have applied by multiple other features. There’s a similar scenario with selection when you move the selected node around – you still keep the selection data, but the visual is gone as the node is re-created.

Then you have another major feature of the Ignite UI Tree – the Load On Demand. This is an extra tricky one actually. While the moving of nodes happens entirely on the client and there’s little that can come in it’s way.. it’s really hard to add a node to a parent one that hasn’t yet populated its children. Yes, you can easily supply the UI, but the underlying data source would still contain no array of child items for you to add to. Of course, one the node populates it’s all good and cool, but as you get told everywhere users are notoriously impatient and in case they do not wait for the node to load it can get quite messy.

Do keep in mind thought, the Ignite UI toolset keeps evolving with not only new features, but general improvements as well and it’s certain Drag&Drop will become more in tune with the rest. For that reason, what I do provide for now would possible become obsolete, but it’s more of a testimony  to the fact that you can enhance and extend Ignite UI controls with very little effort!

Play nice.. or I’ll make you!

So let’s get on with the modifications. The Tree’s events will be our heroes of the day as many if not all major changes are represented with them. I have one very easy fix for maintaining proper selection UI with truly minor effort – as I said it’s all really a matter of picking the right time to hook your code. I figured it’s mildly pointless to update a second selection variable on each change, so instead I will check when a drag is starting if the node in question is selected. Then when the said node is dropped I would simply use the API method provided for selection. This will in essence go and set in the new path for the node:

var selectionDirty = false;

  1. $('#tree').igTree({
  2.     dragAndDrop: true,
  3.     dragAndDropSettings: {
  4.         dragAndDropMode: 'default',
  5.         dragStartDelay: 100,
  6.         revert: true,
  7.         //simple icon-only tooltip
  8.         invalidMoveToMarkup: '<div><span></span><p>&nbsp;</p></div>',
  9.         containment: $("#tree")
  10.     },
  11.     dragStart: function (evt, ui) {
  12.         // check if element is the one selected
  13.         selectionDirty = $('#tree').igTree('isSelected', ui.element);
  14.     },
  15.     nodeDropped: function(evt, ui) {
  16.         if (selectionDirty) {
  17.             //if it was selected, re-select when dropped in new place
  18.             var newNode = $("#tree").igTree("nodeByPath", ui.path + '_' + ui.draggable.attr('data-path').split('_').pop());
  19.             $('#tree').igTree('select', newNode);
  20.         }
  21.         selectionDirty = false;
  22.     },
  23.     dataSource: northwindCategories,
  24.     bindings: {
  25.         textKey: 'CategoryName',
  26.         primaryKey: 'CategoryID',
  27.         valueKey: 'CategoryID',
  28.         childDataProperty: 'Products',
  29.         bindings: {
  30.             textKey: 'ProductName',
  31.             valueKey: 'ProductID',
  32.             primaryKey: 'ProductID'
  33.         }
  34.     }
  35. });

The difference from what you have seen before as code for the tree are the dragStart and nodeDropped events. Because those come in line of the jQuery UI Draggable & Droppable events and they can generally handle interaction between two trees, you won’t see the familiar ‘ui.owner‘ to access the tree as it’s kind of hard to tell which tree should be an owner really :) However, since I’m adding handlers separately that’s okay – I know which tree to call. Also, the tree is helpful enough to give nodes by paths – the latter you can get by combining the event provided path (the parent one) and the ID of the dropped element. The way this handles it right now should be suitable for deeper hierarchy that two as well.

Note: Remember you have the same event arguments as in jQuery UI in addition to the Tree adding some stuff (the element for example), but in the case of the drop the element is the actual target and the draggable from the original arguments is the node being dropped. The results:

An (very low quality) animation to show the comparison between the default selection handling and the enhanced one.

You can take a very similar approach with the checkboxes in bi-state mode Trees to complement that feature, however, the tri-state limitation I feel might be the right design (respecting the parent global state). Up to you really, the option is there.

Load on Demand

As I mentioned it’s nothing that complicated stopping this from being flawless, except the very nature of loading data on demand..it’s not there yet! And if the user doesn’t want to wait things go bad. One very simple way is to decrease the expand delay (time till nodes automatically expand when dragged over) to mere nothing. This might make it work just fine if the service you use is fast enough to get ahead of the unsuspecting user. However, go too low and you risk the user triggering a whole bunch of requests on the drag path and actually making the application slower rather than faster. The solution lies in not letting the user drop on non-populated node. However, that would mean that at some point the node would expand and drop would be possible, but the helper will still be showing invalid location. It’s not such a nice experience to make the user shake the node around to force a drag and re-evaluation of the drop target. If you want just the perfect experience, you can have it with one trick – move the mouse for the user! Yup, basically listen for when the node is populated, check if the user is on top of it, and trigger a mouse move so the validation will be re-done:

  1. var stateHelper = { dropTarget: null, populatingNode: null, draggedNode: null };
  1. $("#tree").igTree({
  2.      dragAndDrop: true,
  3.      parentNodeImageClass: "parent",
  4.      dragAndDropSettings: {
  5.          dragAndDropMode: "move",
  6.          customDropValidation: function (element) {
  7.              // Validates the drop target:
  8.              // Nodes will properly cause targets to expand (and therefore load)
  9.              // But we must not allow actual drop before the data is loaded
  10.              // because we have nothing to add this node to.
  11.              stateHelper.dropTarget = $(this);
  12.              stateHelper.draggedNode = $(element);
  13.              var childNodes = stateHelper.dropTarget.closest('li[data-role=node]').children('ul');
  14.              if (childNodes.length > 0 && JSON.parse(childNodes.attr('data-populated')) === false) {
  15.                  returnfalse;
  16.              }
  17.  
  18.              returntrue;
  19.          },
  20.          expandDelay: 500
  21.      },
  22.      dragStop: function(evt, ui) {
  23.          stateHelper.dropTarget = undefined;
  24.      },
  25.      nodePopulated: function(evt, ui) {
  26.          if (stateHelper.dropTarget && $.contains(ui.element, stateHelper.dropTarget)) {
  27.              stateHelper.populatingNode = ui.element;
  28.          }
  29.      },
  30.      rendered: function(evt, ui) {
  31.          if (stateHelper.populatingNode) {
  32.              // forse re-handling of the drag in case the user manages to
  33.              // shomehow keep his mouse steady when the node expands
  34.              // this will evaluate the target again
  35.              stateHelper.populatingNode.children('a').trigger({
  36.                  type: 'mousemove',
  37.                  pageX: stateHelper.draggedNode.data('draggable').position.left,
  38.                  pageY: stateHelper.draggedNode.data('draggable').position.top
  39.              });
  40.              stateHelper.populatingNode = undefined;
  41.          }
  42.      },
  43.      dataSourceType: 'remoteUrl',
  44.      dataSource: 'http://services.odata.org/OData/OData.svc/Categories?$format=json&$callback=?',
  45.      responseDataKey: "d",
  46.      loadOnDemand: true,
  47.      bindings: {
  48.          textKey: 'Name',
  49.          valueKey: 'ID',
  50.          primaryKey: 'ID',
  51.          childDataProperty: 'Products',
  52.          bindings: {
  53.              textKey: 'Name',
  54.              valueKey: 'ID',
  55.              primaryKey: 'ID',
  56.              childDataProperty: 'Supplier'
  57.          }
  58.      }
  59.  });

As you can see the Tree is bound to the oData Northwind service making calls only when needed, we listen for populated nodes and save them in the ‘stateHelper’ to later trigger the mouse move on them when the UI has been rendered.

A demonstation of how the experience can be improved with load on demand and Drag&Drop

A word on the custom validation function

The thing about this functions is that it will be overridden by the internal tree validation, which makes it the last last chance for you as a developer to return false and prevent a node drop on that target. What this also should tell you is that the internal validation will try to validate actual Ignite UI Tree as a target, so your function is not to enable something the feature won’t allow – no call will be made to it when the target is foreign. This begs the question, can you still use other targets? Yes, the drop events still fire on the drop targets! Take the Simple File Manager as an example – the trick is in the usage of the droppable events instead!

And as you have already seen form above the context of the function is the droppable element and the parameter is the draggable.

Last but not least, if you are wondering how this JavaScript function is being used with the ASP.NET MVC wrappers, the MVC side property accepts a string with the function’s name. What that means is that such functions need to be discoverable on the global scope (on window really), so be careful where you define it:

  1. //rest is omitted
  2.     }).DragAndDropSettings(d =>
  3.     {
  4.         d.ExpandDelay(500);
  5.         d.CustomDropValidation("customValidation");
  6.     })

And then:

  1. <script>
  2.     function customValidation(element) {
  3.         // omitted
  4.     }
  5. </script>

Resources

First let me again remind of the elaborate Ignite UI Tree Documentation and jQuery API Reference. Then don’t forget the awesome samples!

This time I’ll add in the mix a solid ASP.NET MVC demo project ( with everything from here included in with both MVC helper and script-only demos, plus some basic stuff from the previous post). Make sure to check that one out, the animations above don’t do it justice!  You would need at least a trial version of Ignite UI so hit the banner below if you are still lacking one.

Also you can go for fiddling with two demos on JSFiddle:

Stay tuned, for there is still more awesome tricks I have for you – next time we’ll do some updating support!

Summary

This was the second part of the in-depth look at the Ignite UI Tree’s Drag and Drop feature. We covered some of the challenges such functionality presents and how you can overcome them. We improved the selection handling of moved nodes, we made the experience a true joy with Load on Demand. We also gave a little attention to the custom validation function – what is it for, what to expect and what you can do with it. At the end of this, I would like to remind you that this has been a proof of the concept that the controls are flexible enough to let you mold them to your needs with little code and great impact!

Donwload your Ignite UI Free Trial now!

And as always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!


How to add updating to the Ignite UI Tree’s Drag & Drop

$
0
0

Ignite UI Tree’s Drag & Drop with Updating and Undo / Redo

As it turns out the Drag & Drop feature is like the gift that keeps on giving – new year and new enhancement to the functionality! May be because the Ignite UI Tree is flexible enough or maybe because the functionality itself is based on simple Add and Remove API coupled with jQuery UI interactions, both of which are easy to understand, manipulate and extend :)

I’m thinking once you allow a user to move nodes around at one point you might come to the conclusion that some of those changes can be persisted. And as with most user actions - no surprise here! -   mistakes do and will happen. That gets us to yet another point – why not add functionality for the user to undo and redo Drag & Drop operations? Indeed, why not!

And while it does require some custom code, as we are trying to make this interaction feature into an editing one, which is not how it is intended in its current implementation. On the other hand, the custom code is heavily reduced as we have a number of building blocks right there at our disposal. So far we’ve been looking into the usability side in the previous post with Tips on Drag & Drop with the Ignite UI Tree and some basics of the Drag & Drop with the Ignite UI Tree– make sure you check those out as we’ll be building on top of some of the knowledge gained and this time dive into the functionality. I’m not saying the enhanced selection and Load On Demand were not functional, they were, but if you look at it this way – they were tweaks based on the Draggable and Droppable nature implemented by the control. Now remember the other API part? That’s our target now..

Leaving a trace behind

I’ll try not to bore you too much with Add/Remove ‘getting-started’ stuff, as someone else was kind enough to do that – check out the Adding and Removing Nodes Help topic and there are some online samples for those API methods too! The thing that matters the most to us is that those operations create transactions in the underlying data source log and that Drag & Drop uses them. Essentially, we already have a history of the node moves, all you have to do is send it and provide server-side logic to handle the changes. The only thing to keep in mind is that a drop actually calls both methods – removing the original node and adding it to it’s new place. That in turn equals two transactions per drop, rather than one.

The tree also provides an update URL property to set to an endpoint to send transactions to (I’ll use the a similar Tree as the last two blogs for simplicity, again with Northwind’s Categories) :

  1. @model IQueryable<TreeDragDrop.Models.Category>
  2. @using Infragistics.Web.Mvc;
  3. @(Html.Infragistics().Tree(Model).ID("tree")
  4.     .DragAndDrop(true)
  5.     .DragAndDropSettings(d =>
  6.     {
  7.         d.DragAndDropMode(DragAndDropMode.Default);
  8.         d.Revert(true);
  9.         d.CustomDropValidation("customValidation");
  10.     })
  11.     .UpdateUrl(Url.Action("saveChanges"))
  12.     .Bindings(binding =>
  13.     {
  14.         binding.PrimaryKey("CategoryID")
  15.             .TextKey("CategoryName")
  16.             .ValueKey("CategoryID")
  17.             .ChildDataProperty("Products")
  18.             .Bindings(b1 =>
  19.             {
  20.                 b1.TextKey("ProductName")
  21.                 .ValueKey("ProductID")
  22.                 .PrimaryKey("ProductID");
  23.             });
  24.     })
  25.     .DataBind().Render()
  26.     )

Note line 11 where we set the action link. And, of course, this can be in client-only script as well:

  1. $('#tree').igTree({
  2.     dataSource: '/Home/getData',
  3.     dragAndDrop: true,
  4.     dragAndDropSettings : {
  5.         dragStartDelay : 100,
  6.         revert : true,
  7.         customDropValidation : 'customValidation'
  8.     },
  9.     updateUrl: '/Home/saveChanges',
  10.     bindings : {
  11.         primaryKey : 'CategoryID',
  12.         textKey : 'CategoryName',
  13.         valueKey : 'CategoryID',
  14.         childDataProperty : 'Products',
  15.         bindings : {
  16.             textKey : 'ProductName',
  17.             valueKey : 'ProductID',
  18.             primaryKey : 'ProductID'
  19.         }
  20.     }
  21. });

The controller action will have to deserialize the transactions and make sense of them. You have to do the same too I think, here a quick anatomy of a Tree transaction:

  • type – either "addnode" or "removenode" based on the method used. Remember you get both per single drop!
  • tid – generated ID of the transaction, useful for deleting a specific transaction.
  • tdata – all the important goodies are here : the ‘path’ of the node on which the method was performed and its entire ‘data’ record. It also contains a ‘parentPath’ for add operations.

As you can tell from the path we can assert from where the node was removed or to which parent was it added. Don’t forget to validate! That is the reason of the custom function, the Drag & Drop would usually allow much more that it might make sense to your model, especially so if you slack on the binding definitions. The function in this example denies a node from being dropped on it’s current parent or one of the other nodes with the same depth as they are all products. Saving  the changes should generally be as simple as calling a method, however in this case, since the Add Node API method accepts multiple nodes (unlike the remove that is by path which is pretty specific), its path is an array instead of simple string. In a Drag & Drop scenario the add method will only be called with one value so you can go through the changes and normalize the path of the transaction (Drag & Drop would not add multiples) and unify them:

  1. function save() {
  2.     var transLog = $('#tree').igTree('option', 'dataSource').root().allTransactions();
  3.     //normalize transactions
  4.     for (var i = 0; i < transLog.length; i++) {
  5.         if (transLog[i].tdata.path instanceof Array) transLog[i].tdata.path = transLog[i].tdata.path[0];
  6.     }
  7.     //send to server
  8.     $('#tree').igTree('option', 'dataSource').root().saveChanges();
  9. }

Here’s how the controller action looks like:

  1. publicActionResult saveChanges()
  2.  {
  3.      var transactions = JsonConvert.DeserializeObject<List<JObject>>(Request.Form["ig_transactions"]);
  4.      if (transactions.Count % 2 != 0)
  5.      {
  6.          returnnewHttpStatusCodeResult(System.Net.HttpStatusCode.Conflict);
  7.      }
  8.      for (var i = 0; i < transactions.Count; i+=2 )
  9.      {
  10.          // a single drag&drop produces 2 transactions - one removeing from the original spot and one adding to the new one.
  11.          if (((string)transactions[i]["type"] == "addnode"&& (string)transactions[i + 1]["type"] == "removenode") ||
  12.              ((string)transactions[i]["type"] == "removenode"&& (string)transactions[i + 1]["type"] == "addnode"))
  13.          {
  14.              // node was moved moved
  15.              if ((string)transactions[i]["tdata"]["path"] == (string)transactions[i + 1]["tdata"]["path"])
  16.              {
  17.                  //merely a change in order, ignore (this will also skip category moves)
  18.                  continue;
  19.              }
  20.              Product prod = JsonConvert.DeserializeObject<Product>(transactions[i]["tdata"]["data"].ToString());
  21.              var product = db.Products.Where(x => x.ProductID == prod.ProductID).Single();
  22.              int catId;
  23.              if ((string)transactions[i]["type"] == "addnode")
  24.              {
  25.                  catId = int.Parse(transactions[i]["tdata"]["path"].ToString().Split('_').First());
  26.              }
  27.              else
  28.              {
  29.                  catId = int.Parse(transactions[i+1]["tdata"]["path"].ToString().Split('_').First());
  30.              }
  31.              product.CategoryID = catId;
  32.              db.SaveChanges();
  33.          }
  34.      }
  35.      // tell the data source we are cool
  36.      Response.Write("{\"Success\": true}");
  37.      returnnewJsonResult();
  38.  }

As you can see I’ve pretty much made this for the Drag& Drop only (thus the validation if it divides by 2). If you use this in addition to programmatic calls to the Add/Remove API this will require more work ..or the result will be unpredictable. I’m also validating that each two are an add and remove combo and if the path is identical it means this is merely a change in order. Once that is done the actual business part is getting the Category ID (which is the parent part of the path when you have primary keys defined) and assign the one from the add operation to the product.

Ignite UI Tree’s Drag & Drop with Updating

Go there! No, come back! Actually…

Undo that and redo this. It’s those ‘phew’ moments that always make me happy when I realize I haven’t forever lost something. This part however can be either pretty simple ..or somewhat harder to implement than updating depending if you care for nodes being dragged and dropped around under their parent – as in if you care for order. The reason for this is that the paths for nodes generated by the control instead indexes normally, will actually consist of the primary keys if you have some. However, primary keys will only reflect the node’s place in hierarchy but not the order. Since the igTree I’ve been using so far has primary key I figured going the extra mile for a better experience is totally worth it.

var redoStack = [], indexMap = {};

  1. function undo() {
  2.     igtree = igtree || $("#tree").data("igTree");
  3.     treeDS = treeDS || $('#tree').igTree('option', 'dataSource');
  4.     var allTransactions = treeDS.root().allTransactions();
  5.     if (allTransactions.length > 0 && (allTransactions.length % 2 === 0)) {
  6.         var transactions = [allTransactions[allTransactions.length - 2], allTransactions[allTransactions.length - 1]];
  7.         if (transactions[0].type == "removenode") {
  8.             //always need to remove node before adding again!
  9.             transactions = transactions.reverse();
  10.         }
  11.         var deferredIndex = {};
  12.         for (var i = 0; i < 2; i++) {
  13.             var transaction = transactions[i];
  14.             var parentPath = ''; //for root node
  15.             if (transaction.tdata.path.indexOf(igtree.options.pathSeparator) !== -1) {
  16.                 parentPath = transaction.tdata.path.split(igtree.options.pathSeparator).slice(0, this.length - 1).join(igtree.options.pathSeparator);
  17.             }
  18.             if (transaction.type == "removenode") {
  19.                 var parent = parentPath == '' ? igtree.element.children('ul') : igtree.nodeByPath(parentPath);
  20.                 var index = null;
  21.                 if (indexMap[transaction.tdata.path] < parent.children().length) {
  22.                     index = indexMap[transaction.tdata.path];
  23.                 }
  24.                 igtree.addNode(transaction.tdata.data, parent, index);
  25.             }
  26.             else {
  27.                 //save index only after add
  28.                 var theNode = igtree.nodeByPath(transaction.tdata.path[0]);
  29.                 deferredIndex[transaction.tdata.path[0]] = theNode.parent().children().index(theNode);
  30.                 igtree.removeAt(transaction.tdata.path[0]);
  31.             }
  32.             treeDS.root()._removeTransactionByTransactionId(transaction.tid, true);
  33.             redoStack.push(allTransactions.slice(-1)[0]);
  34.             // also remove the two newly created last transaction..
  35.             treeDS.root()._removeTransactionByTransactionId(allTransactions.slice(-1)[0].tid, true);
  36.         }
  37.         $.extend(indexMap, deferredIndex);
  38.     }
  39. }

  1. function redo() {
  2.     igtree = igtree || $("#tree").data("igTree");
  3.     treeDS = treeDS || $('#tree').igTree('option', 'dataSource');
  4.     if (redoStack.length > 0 && (redoStack.length % 2 === 0)) {
  5.         //always need to remove node before adding again!
  6.         //this keeps the order:
  7.         var transactions = [redoStack.pop(), redoStack.pop()];
  8.         var deferredIndex = {};
  9.         for (var i = 0; i < 2; i++) {
  10.             var parentPath = ''; //for root node
  11.             if (transactions[i].tdata.path.indexOf(igtree.options.pathSeparator) !== -1) {
  12.                 parentPath = transactions[i].tdata.path.split(igtree.options.pathSeparator).slice(0, this.length - 1).join(igtree.options.pathSeparator);
  13.             }
  14.             if (transactions[i].type == "removenode") {
  15.                 var parent = parentPath == '' ? igtree.element.children('ul') : igtree.nodeByPath(parentPath);
  16.                 var index = null;
  17.                 if (indexMap[transactions[i].tdata.path] < parent.children().length) {
  18.                     index = indexMap[transactions[i].tdata.path];
  19.                 }
  20.                 igtree.addNode(transactions[i].tdata.data, parent, index);
  21.             }
  22.             else {
  23.                 //save index only after add
  24.                 var theNode = igtree.nodeByPath(transactions[i].tdata.path[0]);
  25.                 deferredIndex[transactions[i].tdata.path[0]] = theNode.parent().children().index(theNode);
  26.                 igtree.removeAt(transactions[i].tdata.path[0]);
  27.             }
  28.         }
  29.         $.extend(indexMap, deferredIndex);
  30.     }
  31. }

We always want to remove first and then add, this is because the way path is created by primary keys as mentioned. If we add a node with the same path (change in order) and then call remove it will delete both the old and new ones. Also notice the two functions deal with somewhat different issues – the ‘undo’ must clean up additional transactions created by the API methods, while the redo takes advantage the latter in order to have transactions back to the log. Furthermore, processing the ‘addnode’ transaction first means more complications when saving an index, since we need the ‘indexMap’ to still contain the old node’s place when undoing the ‘removenode’, so this calls for saving that index to the side and updating it last. Also to gain initial index we hook up to the node dropping event to save it:

  1. $(document).delegate("#tree", "igtreenodedropping", function (evt, ui) {
  2.     indexMap[ui.draggable.data('path')] = ui.draggable.parent().children().index(ui.draggable);
  3. });

Note that caring for node index in this implementation only has meaning on the client-side, but I think it’s nice to have. Check out below the (slightly large) capture of the Undo/Redo in action – note how the transaction log gets cleared when you undo and the ‘Tofu’ node is back under produce, but the redo stack now holds transactions for it. And as expected it shuffles the two moved nodes afterwards as well:

Ignite UI Tree’s Drag & Drop Undo / Redo in action

Resources

As usual the Ignite UI Tree Documentation and jQuery API Reference and detailed samples are at your disposal to draw code and knowledge from!

For the batch updating and undo/redo sample grab the ASP.NET MVC demo project ( with everything from here included in with both MVC helper and script-only demos). Make sure to check that one out!  You would need at least a trial version of Ignite UI so hit the banner below if you are still lacking one.

Also you can go for fiddling right now on JSFiddle: Ignite UI Tree Drag & Drop + Updating demo.

If this has caught your interest and want to know what other cool tricks you can do with this feature – once more the Tips on Drag & Drop with the Ignite UI Tree await!

Summary

Yet another spin of the in-depth look at the Ignite UI Tree’s Drag and Drop feature. With some insight on the API methods used we were able to use this interaction feature as editing one, by implementing batch updating with the help of the underlying data source. Building on top of that you can actually manipulate the log and use the very same API to add Undo / Redo functionality. And since we change the actual log, transactions going to the server for saving will also be in tune! Again it’s all thanks to the building blocks of the feature and their extensibility!

Donwload your Ignite UI Free Trial now!

And as always, you can follow us on Twitter @DamyanPetev and @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Using Ignite UI Controls with WordPress

$
0
0

Ignite UI jQUery controls in WordPress? The options.We’ve stated time and time again that the Ignite UI controls based on jQuery and jQuery UI are pure client-side scripts that are completely server agnostic (meaning they do not depend on the back end). So we keep saying you can use them everywhere, yet time limits the number of platforms we can cover with guides and such. There has been one article on Using jQuery Grid with PHP pretty much. We figured it might be a good time for some more. PHP itself is so tightly integrated with web content (petty much described as HTML-embedded) that every html snippet and sample you find is yours for the taking. Considering that along with JavaScript, they are the two most widely spread scripting languages, I would also assume if you do Web with PHP you probably have a decent experience with both. You can tell

It’s all good and merry when integrating with your own site’s internals, but what if you are developing for something like WordPress? It’s extremely popular and powers a decent chunk of the top websites. The thing about WordPress is that perhaps part of its popularity is due to the fact that it provides a very powerful, but at the same time very user-friendly platform to build sites on – and by that I mean a very easy interface for non-programmers, extensive admin panel, widgetized themes and a plethora of extensions. That also means that if you are in the role of the programmer you have a lot of demand and a platform to develop for Smile

General

I think everyone are/will be quite pleased with the ways you can add functionality to WP – they all have their strengths in certain areas, but more on that later.

Let’s start with the general concept of running an Ignite UI widget in a page – you need jQuery, relevant jQuery UI and Ignite UI scripts (an awesome place to get a list of requirements for each control is to visit the Ignite UI API Reference where each control’s home page gives a detailed ordered list of dependencies or sink yer teeth in the Documentation). As you probably already know the reason I mentioned PHP is because WordPress is built with it – that means you could probably just go and output whatever and wherever you like. However, the beauty of it is that you don’t have to – WordPress kindly offers to handle a lot of that for you:

  • Don’t just output script tags! Use the wp_enqueue_script() function instead – it has jQuery and jQuery UI scripts already registered, so you can simply use something like:
    wp_enqueue_script('jquery');

    There are a ton of plugins or widgets that use jQuery  already and the great deal about this is that the function will not add the library again if it’s already been included - trust me you do not want to double it, I've seen that cause errors, plus you don’t really want to try and check for yourself either, so please use this instead. Also provided are a number of additional settings to provide source location for the script and even define dependencies. Preferably save the Ignite UI control initialization scripts with whatever options you need in a script file and also queue it in the same way.
    On a side note, there’s also wp enqueue style().
  • Be mindful where you include - Note that usually, scripts are added to the head of the document, depending where you do the call, however WordPress will allow mid-page calls to the function that will output the scripts in the footer instead. There’s also option to include in the footer. Use the available action and filter hooks to properly initialize those. Also make use of is_admin() to skip unnecessary script loads in admin panel, unless that is your goal.  
  • No conflict– in the queue function reference there’s a chapter on the jQuery noConflict more– please be aware of that if you are using the default library. Also, in case you are wondering – Ignite UI widgets are indeed properly wrapped in a self-executing function to guarantee safe usage of ‘$’ in private scope. So basically you should only worry about your own code.

What are your options?

So how can leverage the extensibility WordPress provides? Do think of the following as concepts, I might end up doing some, but for now everything below is strictly an idea.

Shortcodes

I think those are by far the easiest to get going ( plug they are decently user-friendly) and WordPress gives you a Shortcode API. As you can see there a short code will be kind of good for adding Ignite UI widgets where you don’t require a lot of settings from the user (you provide most of the defaults) or the control itself is fairly easy to set. That is because, while the API does provide a method to have attributes, asking someone to enter 20-ish or more of them is just wrong. An example for the igRating can look like this:

[ig-rating value="1"]

And through the add_shortcode() you assign a function to this tittle that WordPress will call to replace the tag.

On the flip side, short codes can be added anywhere (pages, posts) so you don’t have to worry about proper hooks for those. The also tricky part is that the attributes don’t do camel casing, therefore you won’t be able to match the Ignite UI widget settings exactly and directly pass the encoded the attributes as options for it.

There are a lot of References at the bottom of every WordPress documantation page (which totally rocks!) so check those out, and here’s a bonus from me with an awesome video guide - WordPress Shortcode API Crash Course.

Widgets

These are also pretty popular, as their main goal is to add content and functionality to sidebars of "widgetized" themes, however since that just means sidebars are registered, so there are ways to add a ‘sidebar’ to your posts and pages (by making a new template, and then using it for new ones). Widgets are not quite as simple to implement, but again there’s Widgets API to the rescue. You have a base class to extend and a few functions to override. The end goal of course is to have something like this in the admin panel:

An example of how a widget would look like added to a sidebar in the WordPress Appearence > Widgets panel.

WordPress will be extremely kind to handle all the database work for you – serializing back and forth, and calling your functions with the ready-to-use arrays. While that totally rocks, just how many settings fields do you think would be acceptable to stick in there? I’d say not too many or you are going to have a really poor user experience. So again much like the short code – good for not-so complicated controls or when used with plenty of defaults you provide instead.

Plugins

And we reach to the most capable option to develop with – as stated the plugins “can extend WordPress to do almost anything you can imagine”. Plugins Docs can be your starting point, but there’s one that is even more interesting – the option register your own settings page with plugin options via the Settings API and have it appear in the admin panel like this:

An example of how plugin settings page can look in the admin panel menu.

And as you’d expect WordPress will abstract away the database plumbing – much like the Widgets options you can output a settings form and the names of inputs will become attributes of the options object you can later on request. The nicest part is that you get a whole page for your control settings ad you can really go wild here – tabs and what not and as many settings as it makes sense.

You will have to worry about hooks and what not, but I’m guessing it can be worth it.

What do you think?

So what do you think would be the most appropriate way to go with this? Do you thing that different solutions fit different controls better? Looking at the Ignite UI toolset, imagine a control being available in WordPress as whether a short code, widget or plugin – as an user, which one are you more likely to use and how? What would you do as a developer?

I’d love to hear some thoughts, so leave a comment down below or @DamyanPetev. If you have already developed anything with Ignite UI and WordPress we’d love to see your work and if you haven’t but you are interested, go for the WordPress documentation and grab yourself some UI fuel from:

Donwload your Ignite UI Free Trial now!

Also stay tuned to see where this ends up!

And as always, you can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

PHP Server-side wrapper for the Ignite UI File Upload

$
0
0
PHP Ignite UI File Upload wrapper

I’ve been recently having plenty of fun shifting platforms like a mad man and I feel it’s time to share some goodness with you all. Let’s have ourselves some PHP (and even if that’s not your thing you still might want to check out the last part)! While we still stand behind our statement that the Ignite UI controls can run everywhere and are server-agnostic, there should really be a tiny asterisk on that one. And what it should state is… well, that you would need custom server side logic to use the File Upload as it only comes with ASP.NET support (handlers for IIS) out-of-the-box. With that in mind, it is true that the control itself is still 100% client-side jQuery, but highly dependent (for now) on communication with the server (I’ll explain why in a bit). So I’ve been on this PHP wave going for Ignite UI Controls with WordPress (for which I’m still gather opinions by the way!) and I thought it would be an awesome idea to have proper PHP wrappers for all Ignite UI controls to ease that up and also to be usable as standalone in other apps. Pretty much I’m making baby-steps towards the original goal (bear with me) and I thought I’d start with something useful like the File Upload:

Ignite UI File Upload in PHP environment?

Take into account that the best part of the control is the AJAX multiple file upload and the progress tracking. First part is taken care of on the client, but the latter needs serious back-end push in most cases where the latest XHR is not supported and I’ll mention those last, for now assume you can’t rely on that. I figured the File Upload progress can’t be that hard – boy, was I in for a ride ..

Limitations, limitations and then some..

So how is file progress handled without client-side access? Well, you can say it takes a lot of communication Smile For the client side to provide a decent experience it needs to know the size of the file, and sadly no good browser will disclose such information to JavaScript (and thus jQuery). So what do you do when only the server can know the actual file size? You ask, of course! The default way of doing so is to fake the file post with special hint to the server (so the backend can grab the size) and reject it. That, however, only flies when you do have access to the actual incoming stream and control over it. The case with PHP is not such – files reach your code ready-to-use. Essentially all you have to do is pick the files you want from the $_FILES superglobal and move them to where you want them saved, for example:

$sucess = move_uploaded_file($_FILES["fileInput"]["tmp_name"] , 'upload/' . $_FILES["fileInput"]["name"]);
echo ($sucess ? "Stored " : "Failed to store") . "in: " . "upload/" . $_FILES["fileInput"]["name"];

Pretty simple! But PHP’s simplicity is both a blessing and a curse - it will upload files to temporary folder and assign temporary names for you, and sadly the actual PHP script you want to use to handle file uploads will only be executed after the file has been completely uploaded or an error has occurred. You can see the problem here, right? Imagine a 100MB file is to be uploaded..so wait for how long to get the size? And the whole  file just to grab the size? And then dump it and upload again? It’s just pointless when you can’t immediately cancel the upload. So no initial size will be available, fine, but progress, what about that? Well cue the drums, in comes PHP 5.4 giving you a fairly usable way of tracking just that. The inner handling of the actual file upload will kindly set you some info in a special variable in the $_SESSION so you can check how the file is coming along. So far so good, you’d imagine, but as long as there’s actual progress going on being posted to one point, another would be best to be constantly polled. That ensures quite the additional work to synchronize between those.

There’s more - remember the part where the file actually makes it all the way to the server, before the realization it’s not welcome happens? Yeah, that also means the progress you are showing to the client is the temporary file being transferred, and only at the end of it will you know if it was successfully saved.

So you get no initial information on size and the outcome is only available after a short wait time? PHP also offers an additional MAX_FILE_SIZE field (see http://www.php.net/manual/en/features.file-upload.post-method.php) which truly confused the living hell out of me. I’m far from the illusion it’s a browser hint ( I mean I think we can almost take  XMLHttpRequest Level 2 (and File API) as granted soon, as it looks like it’s already stable and spreading to even IE10!) so I would assume that fields feature is similar to the actual Session progress field and it somewhat is. While it says it’s supposed to save “users the trouble of waiting for a big file being transferred only to find that it was too large” from my tests(PHP 5.4.7) I find it doesn’t really abort the upload, but the session upload progress does hang.  //end rant

While I have included this as optional functionality, it should be noted this is easily manipulated and while not very functional, it should definitely not be trusted to restrict the size. I could say the progress hang has the potential with some additional logic to be used to detect when the file has gone over the limit and cancel it(from either side), but I haven’t came around to doing that just yet.

Oh yeah, finally you have the session info disappearing from the superglobal as soon as the file is done loading, which is because of the auto-clean. That is again simpler to handle, but harder to manipulate. The thing is, it’s highly recommended to have it on, so I won’t ask you to do otherwise, instead more of the guessing game!

Workarounds, hacks, tricks, etc.

First things first, the settings (in the php.ini or some other way, depending on the hosting and what not..) upload_max_filesize should be set to something reasonable (keep in mind as default it’s just 2MB) and it should be less than post_max_size! Note that there is no workaround for those, other maximum sizes you see are ‘soft’ limits. Note that the Ignite UI File Upload will still do multiple requests for separate files, so the maximum post size can be almost the same as the maximum file size, you don’t need to worry about that. Also, session.upload_progress.enabled should be on (it is by default) and I've left the rest to defaults as well.

So to have a working file upload with PHP on the back-end some patching up of the Ignite UI control was required. Since the actual progress requires the special hidden field before the input (of type file) and we can’t really fake-post here – I have a handler removing the actual file input when registering the file with the server for file size (which for the reason is always initially 0) and adding the special hidden field(s) when the actual post does happen. This is done through scripts emitted from the wrapper. What they also do is play yet another trick on the widget and fix the size in the file information once it becomes available (which is only after the upload has actually started) so the user experience can be mostly unaffected. Oh and the auto-start upload feature totally plays on every weak side of the PHP handling – skipping the initial register and directly going for posting the file and asking for status – which again is unfortunately not really ideal since I can’t map the key from the post with its file name before the upload has ended…therefore requests with the key would return nothing. So I register the file before the widget gets a change to submit in that case, which might not be ideal, but does the job.

One last touch is trying had to maintain a minimal footprint in the session storage by deleting keys that have finished/threw error. However, the equivalent ASP.NET wrapper only holds to such information for a given period of time, which is quite a cumbersome task with PHP, so when you register(pick) a file, but then remove it - no more  requests are made with that key and it sticks around. Here’s an optional way to deal with that :

$(document).delegate("#igUpload1", "iguploadfileuploadaborted", function (evt, ui) {
if (ui.status == 0) {
var key = $("#igUpload1").igUpload("getFileInfoData").filesInfo[ui.fileId].key;
$.ajax({
url: "uploadHandler.php",
type: "GET",
data: {'key': key, 'command': "clear" }
});
}
});

This is totally optional, of course, you could implement something better.

Usage

Why PHP wrappers , you’d ask? Well, so you can have this oh-so-very-nice experience inside your IDE:

Setting up the Ignite UI PHP object in NetBeans IDE

Having pretty much every setting easily available and mostly described (I might’ve failed here) should make using the Ignite UI widgets a joy! And hey, while at it, figured it’s also nice to have the Loader PHP-ready as well! And the options on both classes are identical to the actual jQuery API (so you don’t need PHP specific documentation and so I can serialize them easily Smile ) .

Note you still need to include jQuery and jQuery UI as usual, and then:

<?php
include_once'IG/igLoader.php';
include_once'IG/igUpload.php';
include_once'IG/config.php';
$igLoader = new ig\loader\igLoader("http://cdn-na.infragistics.com/jquery/20122/latest/css/", "http://cdn-na.infragistics.com/jquery/20122/latest/js/", "igUpload");
echo $igLoader;
$igUpload1 = new ig\upload\igUpload("igUpload1", "upload.php", "uploadHandler.php");
$igUpload1->allowedExtensions = ig\upload\Config::$allowedExts;
$igUpload1->mode = ig\upload\UploadMode::Multiple;
$igUpload1->maxSimultaneousFilesUploads = 2;
echo $igUpload1;
?>

The upload handler is the endpoint for requesting status updates, handled by the class I’ve also included. And the third main piece of the package is the upload module that handles file posts. The config contains:

  • allowed extensions  - set to the Upload control above and also verified by the module when saving.
  • accepted Mime types – again verified by the module when saving.
  • file upload path – this is the place the module will try to save to, make sure it exists with write rights
  • max file size – used by the module, can also be assigned to the Upload control, but as explained above its uses are very limited.

And one last thing - security..yeah. it's that part again... well I'm no PHP master and many of the enforced restrictions are based on information that comes from the client. Therefore, they can't be trusted and just in case I've missed something(probably have) you can use the “fileUploaded” event to apply any other checks you want. So in the “upload.php” you can have:

<?php
include_once'IG/igUploadModule.php';
$uploadModule = new ig\upload\igUploadModule();
$uploadModule->fileUploaded("fileSaving");
$uploadModule->HandleRequests();

function fileSaving($sKey, $fKey){
$sessionInfo = $_SESSION[$sKey];
$fileInfo = $_FILES[$fKey];
//check if file should be accepted
returntrue;
}
?>

The function will receive two keys – one for the session info and one for the file info in their respective globals, so you can have the final say if the file should be saved by returning true or false.

Also note that while a blacklist is generally recommended I find a small whitelist to be much more restrictive and closer to the existing control behavior.

The future looks HTML5 bright…

… but not FLASH-y – and by that I mean HTML5 specifications are actively evolving to make any and all client side plugins a thing of the past and file uploading and general file access have been long overdue. Now, though, HTML5 (well, technically, XMLHttpRequest Level 2 is not part of HTML5, but still…) is to offer file access as well as upload progress - pretty much everything you need from the browser (one of the sides that already inherently knows the upload status, but never liked to share so far). So in that sense, I would like to proudly announce that all the hacks and what not are soon going to be completely unnecessary – the Ignite UI File Upload is going HTML5 mode with the next release (that is 13.1) so there will be no more file size requests and no more status requests!

But seriously, that’s not completely true – as I mentioned the standard is not finished and the browser support is not that broad. The point is that the Upload widget itself will fill in the gaps - and the current implementation of the upload module will still verify and accept files just fine, but there will no longer be requests for status. There will be some minor adjustments required to optimize it, but in a up-to-date browser your File Upload widget will produce much less traffic!

Try the PHP File Uplaod with Ignite UI for yourself and if you see somewhere I can improve something or you find a bug do let me know. PHP 5.4 is required. Also once you can get your hands on the new File Upload I will try to provide the PHP wrappers updated for 13.1. Perhaps some WordPress plugin and the same treatment are incoming for other controls (I also might be totally overdoing the promises right now Smile ), but in the meantime, grab your version of the awesome Ignite UI toolset:

Donwload your Ignite UI Free Trial now!

I’d love to hear some thoughts, so leave a comment down below or @DamyanPetev.

And as always, you can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Financial charting with Ignite UI

$
0
0

Financial charting with the Ignite UI Data Chart

Or should I say “How to deliver charts for Technical analysis” and then throw in a little bit of understanding for a good measure. So, we are technical people, no? Then technical analysis shouldn’t sound so distant? Yeah, well, in the field of finance it kind of does… It’s used for forecasting the direction of prices, a discipline in security analysis (probably not the kind of security you would think about first). There’s also quite a bit of theory behind this, but put shortly it’s about making assumptions based on study of past prices and volumes. Still, being able to understand what you are doing is essential, so I’ll try to cover just a little bit – I have the feeling many don’t quite get those and will find it rather interesting. The analysis involves a whole set of values (business opening and closing prices, high and low reached, all for a set time frame and often complemented by volumes). That much data calls for more specialized types of series. Fortunately, the Ignite UI Data Chart has got you covered! Well, at least the technical part, so you can make the finance crowd happy and leave the analysis part to them. The chart control comes with the Financial Price Series in OHLC mode and Candlestick mode and the recent addition of over 35 financial overlay and indicator series to support those.

Get to know the Financial Price Series

These series are a combination of line and bar charts, where each point in the chart is actually a representation of all those 4 price values. But first things first, the data – all the four major values have to do with stock trading price – the high and low are pretty self explanatory, while the open and close are the trading prices agreed upon at the start and end of the business day or whatever time frame is to be plotted. These are all nicely indicated in a singleOHLC bar:

Open-high-low-close chart bar diagram

It has a certain continuity to it so I figured it’d be nice to start with and easy to understand. Besides the bar like OHLC, another way of representing stock trades has been the Japanese Candlestick. While they provide the same information, the difference is in the degree of focus on some or all aspects.

Bulls and Bears

Bear and Bull, Reinhard Dachlauer, Frankfurt - from WIkipediaIt’s beyond me how exactly these two animals came to represent market sentiment / trend, which is exactly the reason to use such type of charting - because “all boats float or sink with the tide”, they say, and also "the trend is your friend". The bull and bear describe upward or downward market trends and with them come the bullish and bearish bars in the charts that help identify trends.

The OHLC has the open and close on specific sides of the high-low bar, so all the data is equally visible, but there is no immediate focus on whether the open was lower than the close or the other way around (note the Ignite UI Data Chart provides color-coding for bullish/bearish). The Candlesticks, on the other hand, put a lot of emphasis on the opening and closing prices and make it quite easily visible and accessible – with both OHLC and bearishness/bullishness explained this should be pretty clear:

A bullish OHLC bar / Candlestick

The candlesticks’ body is symmetrical, but to indicate bullishness it is empty (or differently color-coded), while the bearish is filled (or just darker):

A bearish OHLC bar / Candlestick

Again it's the same data, but rearranged and it's a matter of preference, really. There's also a nice technical side effect from this as I'll describe below. In addition, Candlesticks are much more complicated than this - what I pictured above are actually a Big White and Big Black Candle patterns, but there are the most simple and a lot more advanced patterns used to read/predict trends and if you are interested check out this Candlestick pattern article.

The technical stuff

After the brief theory, we reach the part most of you would be interested in. So the series accept high, low, open and close values in the respective member path property. The bonus I mentioned about the data being equal between OHLC and Candlestick is that you can effortlessly switch between them – there’s a reason why they are two modes of the same series - all you have to do is set the “displayType” property to either “candlestick” or “ohlc” and the chart will automatically react to changes! An example switch code:

$("#priceChart").igDataChart("option", "series", [{
name: "stockSeries",
displayType: "ohlc"
}]);

Following in the footsteps

While looking for a nice data source for my sample I looked into the amazing Finance Dashboard showcase (you can also download the WPF Finance Dashboard). I loved the sample and I wanted to at least partially create something similar for Ignite UI, since the Ignite UI Data Chart comes from the XAML control. Oh, and I totally stole the sample Yahoo! Pipes source, which if I may add is pretty awesome! So with the data out of the way, setting up a chart is rather easy:

$("#priceChart").igDataChart({
dataSource: ds.dataView(),
axes: [{
type: "categoryX",
label: "Date",
name: "xAxis"
}, {
type: "numericY",
name: "yAxis"
}],
series: [{
type: "financial",
displayType: "candlestick",
closeMemberPath: "Close",
highMemberPath: "High",
lowMemberPath: "Low",
openMemberPath: "Open",
xAxis: "xAxis",
yAxis: "yAxis",
name: "stockSeries",
title: "Price Data",
trendLineType: "simpleAverage",
trendLinePeriod: 50
}]
});

As the Financial Series are often displayed with trend line from a variation of a moving averages, do note that as above the “simpleAverage” and the others (exponential, modified, cumulative, weighted) are all moving averages right there at your disposal and can have their period customized.

I also added that accompanying Volume series below in a similar way and a third chart with indicators. Then I figured it’ll also be nice to have some sort of synchronization between the charts to imitate that zoombar’s functionality and event though it’s not quite the same thing, you can easily manipulate them via the World Rectangle. I chose to just use an event from the main stock chart, but this can easily be controlled from a range slider or something similar as well:

$("#priceChart").igDataChart({
windowRectChanged: function (evt, ui) {
// Keep the other two charts in sync
$("#indicatorChart").add("#volumeChart").igDataChart("option", "windowRect", {
left: ui.newLeft,
top: ui.newTop,
width: ui.newWidth,
height: ui.newHeight
});
}
});

As a note, this event is fired a lot so if you intend to use it I suggest you also use the “windowResponse” set to "deferred" on the controlling chart. Careful with cross-linking charts this way – setting new values will also fire the respective charts’ events, just so you know.. and yes the chart is smart enough to ignore values if nothing is really different, but still be mindful.

I actually have a decent set of other “good to know” stuff along the way, so here’s some:

Notes and tips

  • Don’t shy away from using the power of the underlying igDataSource to mold the incoming data to your needs and reduce potentially hulking requests to get filtered sub-sets. As you will be able to see in the demo I’m pre-sorting the data as it comes with the latest first in the array, which would totally make for a backwards plotting if not sorted. The igDataSource got the tools for the job.
  • The “outline” brush property will affect both the outline for candle stick and the shadows (they are drawn in the same stroke with the outline, just so know). However, the outline has nothing to do with the OHLC bars – they, like other line-based series, have no outline.
  • The OHLC bars are, however, affected by the “brush” – in both series the main brush color will be used to indicate bullish candles(the fill)/bars. If you want both empty candlesticks and the ability to switch between modes on the same series, make sure you change the brush color when going to OHLC as the bullish bars there might be left almost invisible!
  • In both series, the the “negativeBrush” property color will be used to indicate bearish candles(the fill)/bars.
  • All the brush properties can be altered at runtime just like changing modes, you can also style the chart using themes!
  • The Financial Indicator/Overlay series are actually 36 in number - an impressive number to chew through. Keep in mind each has a set of required data-binding properties(out of the the already familiar High, Low, Close and Volume) and the some. I’ll go in much greater detail below.
  • Note that with the Data Chart you have freedom to position your indicator series – either as separate charts, or with the main Financial Series using multiple axes.

Financial Indicators

“These indicators are used to help assess whether an asset is trending, and if it is, the probability of its direction and of continuation. Technicians also look for relationships between price/volume indices and market indicators”. Often found above, below and on top of the financial charts. They can be used the same way as any other series found in the igDataChart control and require specific data mapping to stock price values and stock volume. That last part is where it gets extra interesting…

The Ignite UI Data Chart does inherit most of it’s features from the XAML Data Chart, so many things true for the latter and also true for the jQuery one. This means you can also check out the XAML documentation or possibly re-use previous experience with the control. I have, however, went the extra mile on this one to provide you with a complete reference (Ignite UI jQuery API style) of all the Financial Indicator and Overlay series types, descriptions and their relevant data-binding and setting properties! I give you the:

Ignite UI Data Chart Financial Indicators and Overlays (WARNING! Massive, huge table Inside!)

After all, it 36 series and I slightly failed on finding better arrangement, but hey – you are welcome! Smile

The demo allows for switching between different financial indicator series with customizable period.

Also note all the specific properties like periods and such have defaults in case you are wondering what happens if you skip them. For the demo I picked just a few series with a period (usually defaults to 14, with some exceptions I’ve marked in brackets) so I can safely share settings and let you play with them.

Oh and the full view?

It’s not quite like the Dashboard I was after, but this is as far as I had the time to get it to and there’s still plenty of polishing to do. At the very least this is all real data (one year span) and functional enough to play with and give you a good idea what you can do with the Data Chart.

Resources

So besides all the links sprinkled all over, here’s a quick and useful list of info and demos:

Donwload your Ignite UI Free Trial now!

I’d love to hear some thoughts, so leave a comment down below or @DamyanPetev.

And as always, you can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Ignite UI jQuery Grid Column Moving

$
0
0

Ignite UI jQuery Grid with Column Moving feature. Dropdown menu visible on the shot.The Grid is one of Ignite UI’s bread and butter controls and its feature set just.. keeps growing! I guess that fact that Infragistics has done quite a few grid controls over the years helps. Ome of the latest additions is the The Column Moving feature – it is still in a CTP stage for the current release (not for long I should add, 13.1 is coming our way!). It’s a really simple feature at first glance as with other ‘simple’ Grid features they turn out to be not that simple. In all seriousness, there’s barely such a thing as ‘simple’ grid feature because of the way event the smallest change can have impact on multiple other modules. And while there might be some odd end to polish, you can start using the feature now or at the very least get to know it a little.

The Column Moving allows the users to change the order of columns. From a user standpoint things are pretty well rounded – they can simple pick column headers and drag them to the desired position or open up a menu with convenient shortcuts as seen on the shot to the side here. Finally, an additional dialog with columns listed in order to be tweaked (interestingly enough inside is an Ignite UI Tree with drag and drop enabled nodes from the columns).

Getting started

As a developer you’d be pretty happy to find things are kept as easy as can be – enabling such a feature is straightforward – when referencing recourses make sure to include the column moving widget and the add the feature:

  1. $.ig.loader("igGrid.ColumnMoving", function () {
  2.     $("#grid").igGrid({
  3.         autoGenerateColumns: true,
  4.         dataSource: "@Url.Action("People","Home")",
  5.         features: [
  6.             {
  7.                 name: "ColumnMoving",
  8.                 mode: "deferred",
  9.                 type: "render"
  10.             }
  11.         ]
  12.     });
  13. });

And when using the ASP.NET MVC helper you don’t need to define recourses yourself:

  1. @(Html.Infragistics().Grid(Model).ID("Grid1").AutoGenerateColumns(true)
  2. .Features(feature =>
  3.     {
  4.         feature.ColumnMoving().Mode(MovingMode.Immediate).MoveType(MovingType.Dom);
  5.     })
  6. .DataBind()
  7. .Render()
  8. )

Perhaps you’ve notice the subtle differences between the two in term of settings – the Column Moving has two modes and two more modes we call types Smile. The DOM move type will instruct the feature to move only the affected table cells (<td> nodes) in the DOM tree. The other type will move the columns in the data source and then renders the grid again. More on that later on.

The immediate vs. deferred only affects the drag and drop interaction when moving and as you can imagine immediate just moves the column constantly as you drag it around. The deferred only moves on release and in the mean time it provides only an indicator where you’d be dropping that column:

Column Moving feature in deferred mode with an indicator while dragging.

Pretty simple, no? I find the immediate drag & drop reaction more immersive, don’t know just feels like that for me – but it comes at a price of doing a lot of extra movement in between, that can be otherwise omitted. The thing to note here is that the immediate mode (as duly noted in its jQuery API) doesn’t really mix well with the ‘render’ type – imagine dragging around the column header and have the grid underneath re-rendering constantly. It’s just too much of a hassle to attempt to compensate for that, so when mode is immediate, the type will only be ‘dom’ (even if you choose ‘render’).

Control

Out of the twofold nature of the interaction with the feature come two additional settings – you can turn on and off the the additional move menu globally, or both with the drag & drop capabilities on per column basis. The menu is integrated with the Feature Chooser, which is to say that if there are other features using it (such as Column Hiding), disabling the move menu will only remove the relevant parts to that feature. This is done globally though the ‘addMovingDropdown’ setting:

  1.     //....
  2.     features: [
  3.         {
  4.             name: "ColumnMoving",
  5.             addMovingDropdown: false
  6.         }
  7.     ]
  8.     //.....

The per-column settings also let you can disallow the drag and drop functionality for the column (since it’s based on jQuery UI Draggable it means that the widget will not be initialized for the column) in addition to the menu:

  1.         //....
  2.         features: [
  3.             {
  4.                 name: "ColumnMoving",
  5.                 columnSettings: [
  6.                     {
  7.                         columnKey: "Demographics",
  8.                         allowMoving: false
  9.                     }
  10.                 ]
  11.             }
  12.         ]
  13.         //....

Note this doesn’t really fix the column in it’s place – as I explained it merely doesn’t create the draggable widget and Feature chooser menu for that column. However, since other columns can move, they can and probably will push that column out of it’s place. Also the API method can move that column regardless.

Notes, tips and tricks

The feature offers a single method, but a ton of settings and events you can make use of in creating amazing grid experiences. There are some options that probably didn’t make it to the jQuery API page(happens, being CTP and all):

The hidden settings

The column header users drag can be made transparent though the ‘dragHelperOpacity’ as it is exactly that the helper for the jQuery UI Draggable and this directly corresponds to the opacity property.

There are two tolerance settings for this feature and they are both only regarding the Drag&Drop interaction side of Column Moving – besides letting you dragging the header around the feature is also kind enough to scroll the grid for you when you go near the edge. The edge is actually a distance threshold defined by the ‘movingScrollTolerance’ property (length (in pixels) below which horizontal scrolling occurs) and supported by ‘scrollDelta’ that dictated how much should be scrolled each time.

Then you also have the ‘movingAcceptanceTolerance’ which is how the feature checks if you are close to a possible column drop point and the column/indicator should be moved there. I think a visual explanation will be great for this. The default accept tolerance is 20px which means that if the edge of the helper moves closer that that to some edge, that edge will be accepted as target position for column moving, see what I mean:

The Column Moving accept tolerance of 20px visualized as a green-ish area, that once crossed by the drag helper will cause a colum/incidator move.

There’s a very important note for the tolerance settings – use wisely and with caution– imagine what will happen if you crank up the tolerance too much, like wider than your columns? The feature will totally go crazy and drag column moving will produce highly unpredictable results! I’d say those 20px as pretty confortable, but hey with great settings power comes great… you get the point!

The ‘hideHeaderContentsDuringDrag’ which is why the actual column header above is empty when dragging.

Multi-Column headers and hierarchy

The Column Moving acts really well with this (also new) feature and also plays nice with the Hierarchical version of the grid. The drag helper/header is restricted within the confines of it’s respective grid (which prevents child-parent moves in the Hierarchical Grid) and won’t be accepted outside it’s column group, so the Multi header settings are respected. There are some gotcha-s here – the API move method will believe you more that the user and attempt to move whatever you tell it to wherever – yes you can move entire header by identifier, but careful with indexes – they include the group and vary inside it as well. I might do a whole blog on that later on, but for now just thoroughly test if you combine those features.

Performance

So, back to those move types. As you might've guessed there’s a reason why the move type is exposed to the developer – it has performance implications because of the ways the actual moving is performed. Remember how data is laid in the HTML table – by columns? Of course not – it’s by rows. That means finding the right cell, detaching it and re-attaching it before/after a different target cell. That’s not that bad, but consider it is done for every row each time you move a column! It can get pretty intensive.

The re-render, on the other hand, destroys most of the grid layout and based on the already updated data source renders the data table again. So how’s that for intensive? Well, it depends kind of… Our developers noted this and I did some very quick tests to confirm this:

As a general rule of thumb, DOM Column Moving is faster for most browsers, but (surprise, surprise!) the ‘render ‘ is actually faster for IE. And, yes, I’ve tested IE10 thinking there’s probably some sort of an error, but even with small sample numbers the difference was so huge that there’s no way it’s wrong. No idea why – is it the querying of a whole bunch of elements or the DOM manipulation that’s the bottleneck, but it turns out re-rendering is faster for IE.

But, hey, that’s just interesting info – there’s no reason for you to worry or even consider this an issue – there’s an easy solution – please, please use Paging! With reasonable page size the time it takes to move a column of 2000 rows takes a plunge down to a snappy, pretty much instant, 50ms range. Trust me when I say it makes all the difference and it’s an absolute must if you plan on a lot of data. And if for whatever reason you need it all in a single view(page) then enable Row Virtualization! Paging kind of delivers the same functionality and the conclusion is an old one – virtualization is totally awesome with big data and a virtualized grid handles thousands of records like a boss!

Making use of events to enhance functionality

Personally there’s something that bugs me about the immediate mode of the feature.. When dragging columns around in a very demo grid, with all healthy mix of easily distinguishable text/numbers mix in the columns, it’s all good and merry. But when you hit a grid with like all numbers – like it’s very likely to happen in some scenarios – I find it really hard to track my column around. There’s no indicator with immediate, the only notice really is the empty header if hasn’t been disabled or the same text following you around. Good thing the Column Moving Events are many and give you precious information needed to control and/or enhance functionality.

Well I figured I can show you how to events and enrich the experience at the same time. I’ve decided to cheat a bit and steal the Sorting feature indication by highlighting the column currently moving with the following class:

  1. <style>
  2.     .ui-iggridtd.ui-state-moving {
  3.         background: 0;
  4.         background-color: #c2e8f8;
  5.         color: #333;
  6.         font-weight: normal;
  7.         text-shadow: 01px0rgba(255,255,255,0.8);
  8.         border-color: #b3e2f6;
  9.         border-width:  1px;
  10.     }
  11. </style>

Two things to note here – this is almost identical to the highlight Sorting uses, just renamed so it won’t mess with the feature’s CSS, but if you absolutely know you won’t be using Sorting or it’s highlight, it’s safe to just apply ‘ui-state-highlight’. And secondly, if you are using it with sorting CSS you might want to consider some different colors. Having said that, applying the class is extremely easy:

  1.         features: [
  2.             {
  3.                 name: "ColumnMoving",
  4.                 mode: "immediate",
  5.                 columnDragStart: function (event, ui) {
  6.                     ui.owner.element.find('tr td:nth-child(' + (ui.columnIndex + 1) + ')').addClass("ui-state-moving");
  7.                     colMovingKey = ui.columnKey;
  8.                 },
  9.                 columnDragEnd: function (event, ui) {
  10.                     var grid = $(event.target).data('igGrid');
  11.                     var newindex = grid.options.columns.indexOf(grid.columnByKey(colMovingKey)) + 1;
  12.                     grid.element.find('tr td:nth-child(' + newindex + ')').removeClass("ui-state-moving");
  13.                 }
  14.             }
  15.         ]

The drag start and end events correspond directly to the jQuery UI Draggable events, and they even relay most of the common arguments. The other events include a column moving and moved events to react before and after a move, and then a whole bunch of events related to the additional menu and dialog.

The above snippet is only fit for a very basic layout (the demos include a somewhat acceptable version with multi-column headers as well, so check them out). The result looks something like this – just imagine a much bigger grid and better image quality:

Using the events of Column Mvoing to add a highlight while dragging.

As I always say, it’s not as much about modifying the feature, but showing you can quickly apply enhancements and control at various points of interest. And it’s that easy!

Resources

So besides all the links sprinkled all over, here’s a quick and useful list of info and demos:

Donwload your Ignite UI Free Trial now!

I’d love to hear some thoughts, so leave a comment down below or @DamyanPetev.

And as always, you can follow us on Twitter @Infragistics and stay in touch on Facebook, Google+ and LinkedIn!

Viewing all 45 articles
Browse latest View live