Request: SPRequireUnique with dataview webpart

May 31, 2011 at 10:49 PM

Although I'm fairly new to the SPServices, I really like the simplicity and JQuery integration behind it - I'm coming from SPAPI which worked well but required a bit more work, so thank you for that.

One thing I'd really like to have is the ability to use SPRequireUnique on dataform webparts during New/edit. The issue is that you cannot pass any listID to the function or target specific elements.

I'll probably write a function that mimic's your SPRequireUnique, but I was wondering why this wasn't built-in with the many great functions. Looking at the source code it seems that you would have to pass the listID instead of looking it up from URL and the target could be the object itself as triggered by jQuery.

In short you would have something like:

$("#FieldID").SPServices.SPRequireUnique({listID: "1231298-12312-1312312", columnStaticName: "Title"});

 

As simple as this seems, I'm just not sure why it wasn't part of this code? I'll be more than happy to share my final code and maybe see it integrated :)

Coordinator
May 31, 2011 at 10:53 PM

There's no need to pass the listID, as I figure out what list you're working with from the context. This means that your customized forms simply need to live in the /Lists/ListName context, or if it's a library, then /LibraryName/Forms.

Make sense?

M.

Coordinator
May 31, 2011 at 10:56 PM

Oh, and I missed that you were doing

$("#FieldID").SPServices.SPRequireUnique({listID: "1231298-12312-1312312", columnStaticName: "Title"});

No need for that selector, as the function simply finds the appropriate element for the columnStaticName in the DOM.

$().SPServices.SPRequireUnique({
  columnStaticName: "Title"
});

as shown in the docs.

M.

May 31, 2011 at 11:07 PM
Edited May 31, 2011 at 11:42 PM

Since I made a quick hack to the functions - I'll post my starting code too (probably needs to be tweaked / optimized)

 

// Function which checks to see if the value for a column on the form is unique in the list.
$.fn.SPListRequireUnique = function (options) {

	var opt = $.extend({}, {
		listID: "",									// List ID
		itemID: null,								// The iem being edited
		columnStaticName: "Title",					// Name of the column
		duplicateAction: 0,							// 0 = warn, 1 = prevent
		ignoreCase: false,							// If set to true, the function ignores case, if false it looks for an exact match
		initMsg: "This value must be unique.",		// Initial message to display after setup
		initMsgCSSClass: "ms-vb",					// CSS class for initial message
		errMsg: "This value is not unique.",		// Error message to display if not unique
		errMsgCSSClass: "ms-formvalidation",		// CSS class for error message
		showDupes: false,							// If true, show links to the duplicate item(s) after the error message
		completefunc: null							// Function to call on completion of rendering the change.
	}, options);

	// Get the current item's ID from the Query String
	var thisList = opt.listID;
	var thisID = opt.itemID;

	// Set the messages based on the options provided
	var msg = "<span id='SPRequireUnique" + opt.columnStaticName + "' class='{0}'>{1}</span><br/>";
	var firstMsg = msg.replace(/\{0\}/g, opt.initMsgCSSClass).replace(/\{1\}/g, opt.initMsg);
	
	// We need the DisplayName
	var columnDisplayName = $().SPServices.SPGetDisplayFromStatic({
		listName: thisList,
		columnStaticName: opt.columnStaticName
	});
	var columnObj = this;
	$(columnObj).parent().append(firstMsg);

	$(columnObj).blur(function () {
		var columnValueIDs = [];
		// Get the columnDisplayName's value
		var columnValue = $(this).attr("value");

		// Call the Lists Web Service (GetListItems) to see if the value already exists
		$().SPServices({
			operation: "GetListItems",
			async: false,
			listName: thisList,
			// Make sure we get all the items, ignoring any filters on the default view.
			CAMLQuery: "<Query><Where><IsNotNull><FieldRef Name='" + opt.columnStaticName + "'/></IsNotNull></Where></Query>",
			// Filter based on columnStaticName's value
			CAMLViewFields: "<ViewFields><FieldRef Name='ID' /><FieldRef Name='" + opt.columnStaticName + "' /></ViewFields>",
			// Override the default view rowlimit and get all appropriate rows
			CAMLRowLimit: 0,
			completefunc: function(xData, Status) {
				var testValue = opt.ignoreCase ? columnValue.toUpperCase() : columnValue;
				$(xData.responseXML).find("[nodeName='z:row']").each(function() {
					var thisValue = opt.ignoreCase ? $(this).attr("ows_" + opt.columnStaticName).toUpperCase() : $(this).attr("ows_" + opt.columnStaticName);
					// If this value already exists in columnStaticName and it's not the current item, then save the ID in the array
					if((testValue === thisValue) && ($(this).attr("ows_ID") !== thisID)) {
						columnValueIDs.push([$(this).attr("ows_ID"), $(this).attr("ows_" + opt.columnStaticName)]);
					}
				});
			}
		});
		var newMsg = opt.initMsg;
		$("span#SPRequireUnique" + opt.columnStaticName).html(newMsg).attr("class", opt.initMsgCSSClass);

		$("input[value='OK'], input[value='Save']").attr("disabled", "");
		if(columnValueIDs.length > 0) {
			newMsg = opt.errMsg;
			$("span#SPRequireUnique" + opt.columnStaticName).html(newMsg).attr("class", opt.errMsgCSSClass);
			if(opt.duplicateAction === 1) {
				this.focus();
				$("input[value='OK'], input[value='Save']").attr("disabled", "disabled");
			}
			if(opt.showDupes) {
				var out = " " + columnValueIDs.length + " duplicate item" + (columnValueIDs.length > 1 ? "s" : "") + ": ";
				for (i=0;i < columnValueIDs.length; i++) {
					out += "<a href='DispForm.aspx?ID=" + columnValueIDs[i][0] + "&Source=" + location.href + "'>" + columnValueIDs[i][1] + "</a> ";
				}
				$("span#SPRequireUnique" + opt.columnStaticName).append(out);
			}
		}

	});
	// If present, call completefunc when all else is done
	if(opt.completefunc !== null) {
		opt.completefunc();
	}
}; // End $.fn.SPServices.SPListRequireUnique

May 31, 2011 at 11:12 PM
Edited May 31, 2011 at 11:53 PM

Basically I'm trying to use this on a dataform webpart - So that if you had a page like /myNewStuff.aspx - within that page you created a custom list form such as: http://office.microsoft.com/en-us/sharepoint-designer-help/create-a-custom-list-form-HA010119111.aspx

Then you can call the above to make sure that the title is unique. Makes sense?

 

I appreciate the speed of response :)

 

 

edit:

I see what you're saying about:

 

$("input[Title='" + opt.columnDisplayName + "']")

But that only works if you're using a proper sharepoint element - If you're creating your own form, then this will no longer work, hence the idea of using the element to which the jQuery Object refers to - This can go both ways really...

Maybe it could be enhanced by looking at the jQuery Object and determining what type it is?

 

edit 2: The check should be probably similar to:

 

if(this[0] == null){
	var columnObj = $("input[Title='" + columnDisplayName + "']");
}else{
	var columnObj = this;
}

Except I don't know what that looks like when you got into a plugin's inner function - The above will not work if I have the function as:  $.fn.SPServices.SPListRequireUnique

Jun 1, 2011 at 12:08 AM

Looks like 'this' element isn't available since the SPServices namespace isn't fully following the namespace convention - http://docs.jquery.com/Plugins/Authoring#Plugin_Methods

I guess can still save the object when it is initialized and simply re-use it later on.

Coordinator
Jun 1, 2011 at 3:22 AM

Keep in mind that SPServices is designed to work with the most common SharePoint constructs. If I tried to cover all of the variability a developer can introduce, not only would I not be able to cover all those variations, but there would be unneccessary overhead for those unusual conditions.

Obviously, since SPServices is open source, you're wlecome to take it and do what you will with it.

Selectors like

$("input[Title='" + opt.columnDisplayName + "']")

still work fine with customized forms, as long as you are using the SharePoint field types. If you build your own field types, then you probably are sophisticated enough not to need SPServices.

M.

Jun 1, 2011 at 2:43 PM

I think you're completely right in your statement - I just thought that targeting a list could've been part of the functionality - I'll make sure to keep updating this post as I figure more stuff out.

One question I had for you was for the actual 'this' object, would it be smart to store it during initialization and be able to use it in sub-functions?

Thanks again for chiming in.

Coordinator
Jun 1, 2011 at 2:45 PM

Which 'this'? Can you give an example?

M.

Jun 1, 2011 at 6:42 PM

When you execute:

$().SPServices.SPRequireUnique();

or

$().SPServices()
'this' represents the jQuery element on which the function was called. In the first call, 'this' is lost within the sub-functions. In the 2nd call, 'this' is correctly the jQuery element. I'm guessing it needs to be part of the hard-coded variables of the plugin in order to be available and consistent across the plugin functions

Coordinator
Jun 1, 2011 at 6:47 PM

What I was wondering was where you'd need it, i.e., a specific example.

I've built the functions the way I have because I feel that it makes little sense to call them on an element; they operate independently.

M.

Jun 1, 2011 at 8:28 PM

Right, I guess this was coming back to targeting a specific element passed during the call, instead of the input[title=] element

Coordinator
Jun 1, 2011 at 8:38 PM

SharePoint uses different HTML elements and often script to manage each field type and the functions in SPServices are targeted for use with specific field types. The way you specify which column you'd like to work with is by passing info via the function's options.

For a function like SPRequireUnique, the logic would be considerably different for column types other than Single line of text, and I haven't built any capabilities into the function for other column types. (This is based on the lack of request for any others.)

The reason I'm looking for a specific example is so that I can understand whether there's a need for additional capability in SPServices or if you're just speaking theoretically.

M.