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
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:
- 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.
- 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.
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.
Comments
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>");
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.
@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."
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.
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 =/
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>");
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
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.
Wow, that looks awesome! I really like what your are doing with this, I'll be testing this in our lab and report back.
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.
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}
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?
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
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
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');
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.
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.
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.
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.
John, What line I need to change/add?
var getCriteriaValue = function (value) {
if (value.length < 3) value="xxxxxxxxxx";
[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!