Home Cireson Uploads
image


IT Monkey will place code here as examples of what Cireson's consulting team has to offer as well as examples for public consumption to benefit the Microsoft System Center community as a whole.

DISCLAIMER

All files and projects located here come as is and without any warranty or support. We will attempt to improve the projects as time goes on based on customer and community demand. Comments and improvements are welcome as well as customization requests. Your use of these Cireson Uploads is subject to our Terms of Use.


Cireson's support team has no information on these projects outside of what you have available and will not provide support for these enhancements, extensions, and scripts.

Dont forget to checkout solutions uploaded by our customers, partners and community members here.

Transforming the Grid Picker

john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
This is very much a work in progress, or rather a work just begun, but I thought I would share it anyway.

The other day, I was thinking about the way we implement server-side filtering in Request Offerings. The usual trick is to create an optional text field and then a query results prompt where the criteria includes the value of the text field. I was wondering if I could hijack these controls, and use them to create an autocomplete control, i.e. a text field in which the user starts entering some characters from the name of what they are looking for.

To test this, I built a form which is looking for two inputs: an author and a location. The current solution looks like this:


I decided to add two more prompts to the form to serve as markers which would tell me where to start building the new controls.
The Request Offering definition now looks like this:


Rendered in the portal this looks like:


Now all that remained was to add the custom javascript code to locate the markers, and hide them along with the text and grid controls. I then used the definition of each control to create a new Autocomplete control.

Here's what the original form looks like now. I hope you will agree that it looks a lot neater than the original.
Rather than simply show the Display Name in the drop down, I decided to join all the properties which had been selected in the query results control. For the author control, I selected the Display Name and Domain. Here's what it looks like in the picker:


You can add, remove or reorder these strings by modifying the display settings on the request offering prompt.

I could have created a template based on the selected columns and applied that, but I decided to keep it simple initially.

I have attached the custom code I used to generate these controls. It is not fully tested. In fact I have only run it in Chrome so far. I also have no idea how this will work on multi-page forms.

Some limits:
  1.  I have assumed that there will only be one token in the selection criteria. An autocomplete control will only provide a single input anyway, so it seems a reasonable assumption.
  2. The validation error is hidden. I should replicate or move the input to another part of the form, but I have not done that yet.
If you decide to try it out, I would love to hear how you get on with it. Remember this is just a first attempt and is not really ready to be implemented into production.

Installation:
Add the lines from the custom.js file to the custom.js file in the folder CiresonPortal\CustomSpace
Copy the file custom_ROAutocomplete.js to the same folder.
«13456789

Comments

  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    Found an issue on line 88.

    The question-container class is used to scan for the input values. These controls should not be children of that class.

    Change the line to read:
        queryResults.after("<div class='row custom-item-picker'><div class='col-md-6 col-xs-12'>" + controlLabel + "<input id='ac" + targetId +"' style='width: 100%;' /><div style='margin-bottom:25px;'><i>" + controlHint + "</i></div></div></div>");


  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭
    edited May 2017
    Thanks John. This looks promising. Anyone test it? I don't have a test environment.
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    This look awesome, thanks a lot @john_doyle !

    I've implemented it in my test env, and everything look really good. Testing it on Firefox, and so far it works nicely. One thing I did was setting controlHint as placeholder on the new kendo auto complete box, instead of having it below the box, which I think gives a cleaner look.



    I especially like, that you don't actually use the query prompt functionality at all, but just get the required data you need from it, such as class / projection, criteria set in the RO builder etc., and instead use the Cireson API to fetch the objects and sort through them. As I understand, this means that we do not have the limitation of query result max size, and therefore the "contains token" functionality now becomes viable also when you have a large user base. Am I getting this right?

    And of course one has to remember to now set the "criteria box" as optional and not required, as that would prevent the user from saving the RO.
  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭

    @Konstantin_Slavin-Bo would you mind sharing the change you made? "One thing I did was setting controlHint as placeholder on the new kendo auto complete box, instead of having it below the box, which I think gives a cleaner look."

  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    Hi Konstantin,

    I like what you did with the placeholder.
    This control should help with the limitations on the query result, because the data is filtered on the server and not on the client. The user can type in as many characters as they need to limit the results coming from the server.

    For the moment, it will only select one object. I need to add some code so that it can be used to select multiple objects.
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    @john_doyle

    Yeah, I thought so. This is very nice for us, because we have been limited to using "equals token" in our criteria, as the blank box would return every user, so users would have to type in the full username of a user, and couldn't utilize any filtering functionality.

    Yeah, I noticed the "limitation" regarding only one selection, but I can't really see right now, how multiple selections would be done with this layout?

    @Adrian_Mataisz
    Yes of course, I changed line 88 to:

      queryResults.after("<div class='row custom-item-picker' style='padding-bottom: 25px;'><div class='col-md-6 col-xs-12'>" + controlLabel + "<input id='ac" + targetId +"' style='width: 100%;' /></div></div>");

    This removes the div for controlHint below the new box, and changed line 112 to:

        placeholder: controlHint,

    to add the text as placeholder in the new box.

    PS: the code-formatting is close to useless =/


  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    @Adrian_Mataisz ; He changed the placeholder so that it uses the controlHint variable, like this:

            dataTextField:"DisplayName",
            highlightFirst: true,
            placeholder: controlHint,
            dataSource: new kendo.data.DataSource({

    You will have to remove the hint from the HTML code on line 88

    queryResults.after("<div class='row custom-item-picker' style='margin-bottom:15px;'><div class='col-md-6 col-xs-12'>" + controlLabel + "<input id='ac" + targetId +"' style='width: 100%;' /></div></div>");



  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    Ah yes, margin-bottom: 15px looks more right than padding it with 25px ;) Thanks!
  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    @Konstantin_Slavin-Bo; You define a separator on the control. When the user has selected one result, the control adds the separator and then you can start typing another name. I just need to check that I have the code in place to bind multiple answers and add the separator definition if the query results control is set to allow multiple selections.
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    @john_doyle Yes of course, that's actually pretty obvious - I think I need to go home and get some rest after a long day! I look forward to see where this goes. I will try to take a look at it, if I get the time and/or any ideas.
  • Adrian_PaechAdrian_Paech Customer Advanced IT Monkey ✭✭✭

    Hey John,

    Really like this solution! however the search upon typing something in the box seems to be a bit clunky. Sometimes it searches and sometimes it doesn't. Also, it seems to take up to 2 seconds before the @custompicker gets converted to the alternate view.

    Is this because it is waiting for the data to load for the query results picker?

    Regards,

    Adrian

  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    Hi Adrian,
    I have made some changes to the code which may help.

    I also added a new control to handle cases where you want to allow the user to select multiple objects. This uses the Kendo MultiSelect control. It looks like this:


    The new control uses the tag @customMultiPicker on the Request Offering forms.

    The form transformation occurs when the first asynchronous call to the server completes. I may have to use a different method if you find that this is always significantly delayed.
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    @john_doyle,
    Wow, that looks awesome! I really like what your are doing with this, I'll be testing this in our lab and report back.

    Really like this solution! however the search upon typing something in the box seems to be a bit clunky. Sometimes it searches and sometimes it doesn't.


    This is because the API isn't queried on less than 3 chars. I'm not aware if this is a limitation of the API (the end-point used is undocumented / seems to be internal), or if John included this to prevent broad queries / large returns of data? I'm guessing the latter.
  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    edited May 2017
    Yes, I have hard-coded it to only start the search if there are more than 3 characters.
    You'll find it in the definition of the control.
    minLength: 3,

    What I could do is add some code to allow you to specify this value in the tag.
    So the tag would be @custompicker(3). I'll think about adding that. I could also expose a value for the width of the control.
    Maybe it would be better to format the tag like @custompicker{minLength: 3,colSpan: 8}
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭

    Maybe it would be better to format the tag like @custompicker{minLength: 3,colSpan: 8}

    Yeah, that would be a nice way to do it. It would enable us to customize some settings without fiddling around in the js. I personally don't see a need for the changing the colSpan property, but of course there could be those who can.

    I have a couple of questions to the code: Why do you define separate ajaxStop() handlers, instead of just using one and putting both if-statements in there? I guess the performance difference (if any) would be minimal, though? Also, you wrote that the transformation occurs when the first call to the server completes, but doesn't ajaxStop() fire when all Ajax requests completes? https://api.jquery.com/ajaxStop/ Or am I misunderstanding something?

    I don't know whether you could catch the query result request with ajaxComplete() instead, and thereby make sure to do the transformation as soon as the query result is ready?


  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    @Konstantin_Slavin-Bo
    the only reason that i used two ajaxStop handlers was because I copied and pasted the code for the autocomplete to create the MultiSelect control. I have come up with quite a few more ideas for these tags, so I will roll them all into one handler.

    Some more potential future tags for the toolbox:
    datePicker (allow user to save a date from a calendar into a datetime field or a string field)
    dateRange (link two date controls so the output of the first is the lower limit for the second)
    dragDropFile (let user drop files onto the page to add attachments)
    renderImage (put a picture on the form)
    column{n} display the next 'n' controls in columns across the page
  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    More ideas:
    advancedList{queryId} like a simple list, but populated by a query defined under Admin Settings
    radioButton ... convert a list into an array of radio buttons
  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭
    I just tested but for some reason, it takes at least 30 seconds for the code to apply. Initially, displays all the fields/text and after about 30 seconds shows up correctly. Is this the normal behavior?
  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    @Adrian_Mataisz Evidently I need to move away from adding the handler to ajaxStop.
    Can you try changing the ajaxStop handlers? Try the suggestion from @Konstantin_Slavin-Bo and replace the calls with this code:

    $(document).ajaxComplete(function () {
        $(this).unbind('ajaxComplete');


  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭
    Thanks, much better now.
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    Those are some awesome ideas, John! It would significantly improve the UI of the portal, something we could all use. And I really like that Cireson is doing this as a "community project", where we can give our input and help out. I don't know if a shared repo would be of interest, to allow the community to contribute even more?

    What do you want to do differently with the datePicker? The existing date picker already shows a calendar and can save the data into a datetime and string field.
    With the dateRange, you should allow for the two dates to be saved to two different fields.
    Isn't it possible to insert an image on the form already with an <img> tag?
    column{n} would be something like Customizing the layout of advanced request offerings, right? That would be excellent to have in a easily-configurable tag!
    advancedList - what do you have in mind for a query here? SQL query, SCSM query?

    I also noticed some maybe-maybe-not unwanted functionality: The autoComplete box will show all results, when you search for something and the delete the string. I understand, that at this point, the whole result is returned to the browser, but I don't know if this side-effect is wanted or not? Maybe it could just be fixed by not showing the dropdown when the box is empty / is less than minLength? But again, I don't know if it actually needs fixing.

    @Adrian_Mataisz - I don't experience that behavior, what browser are you using? I am only testing this on a small query of about 100 objects, though, so that maybe be the reason for it loading rather quickly for me.

  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    @Konstantin_Slavin-Bo I noticed the 'return all results if the filter is removed' behaviour myself and tried to stop it by adding the enforceMinLength parameter, but it seems to be ignoring it. I'll find a solution.

    Re: advancedList:  You can define queries for dashboards under Admin Settings in the portal, and add your own data sources. We could use this mechanism to populate lists of options to the end user instead of having them hard-coded in the database.


  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭
    I agree is a little bit clunky. Sometimes if you select an object and you go back to delete it it will show the 3 green dots seaching again and may take 10-20 seconds before returns your search (in our case is searching about  8500 users) , other times if you type another name it will show you the dropdown with the objects meeting your search criteria, is so random. 
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    @john_doyle
    Ah, that's what it's for. Weird that it doesn't work. Maybe you can use the filtering event as described in the third example here? http://docs.telerik.com/kendo-ui/api/javascript/ui/autocomplete# events


    @Adrian_Mataisz
    Hm, it sounds like I need to test this out with a (much) larger data set. 

  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭
    I noticed it too: " I noticed the 'return all results if the filter is removed' behavior myself and tried to stop it by adding the enforceMinLength parameter, but it seems to be ignoring it. I'll find a solution."
  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    edited May 2017
    It's a bit of a hack, but I got around it by adding this to the getCriteriaValue functions:

        var getCriteriaValue = function (value) {
            if (value.length < 3) value="xxxxxxxxxx";

    Basically it sends a fake filter which matches nothing.

    It would be better if it simply didn't make the call. I suspect we need to update the Kendo UI library we are using in order to get the enforceMinLength parameter.

  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭

    John, What line I need to change/add?

     var getCriteriaValue = function (value) {
    if (value.length < 3) value="xxxxxxxxxx";

  • john_doylejohn_doyle Cireson Support Ninja IT Monkey ✭✭✭✭
    There are two getCriteriaValue functions defined in the file, search for them and add the 'if' statement on the next line.
  • Adrian_MataiszAdrian_Mataisz Customer Advanced IT Monkey ✭✭✭
    Thank you John. Whit this "hack" is pretty neat.  
  • Konstantin_Slavin-BoKonstantin_Slavin-Bo Customer Ninja IT Monkey ✭✭✭✭
    edited May 2017
    I tested it out by adding the filtering event to the autocomplete component and then catching it to preventDefault() if filter value is empty, and it seems to be working.

    [snip]
        placeholder: controlHint,
        filtering: function(e) {
          var filter = e.filter;
         
          if(!filter.value) {
            e.preventDefault();
          }
        },
        dataSource: new kendo.data.DataSource({
    [snip]

    Edit: enforceMinLength is not necessary anymore with this.
    Edit2: Haven't tested it with multiselect btw!

Sign In or Register to comment.