Setup
I have a list of drop down lists that are all identical (think row item in a grid). They can be added dynamically and all contain the same list of items. My client has requested that items that are selected in one list should be disabled in all of the other lists.
Everything works except the newly added lists do not have their items disabled (the existing ones do).
The Code
This is broken into a series of function calls but I have munged it here for easier viewing:
function DoTheStuff()
{
enableAllTheDropDownItems(); //This works
var allLists = $(".fooSelections");
allLists.each(function(itemIndex, selectList){
var selectedItems = $(".fooSelections option[selected='selected']");
selectedItems.each(function(index, element) {
var selectedValue = $(element).val();
var parent = $(element).parent();
//skips an item if it is the selectedItem of the current list
if(parent.is(selectList)){
return true;
}
$("option[value*='" + selectedValue + "']", selectList).attr('disabled', 'disabled');
});
});
}
I am using Templates (Editor/Display) to show this partial view but essentially the Razor syntax that creates each Select list looks like this:
#Html.DropDownListFor(m => m.FooId, new SelectList(Model.Foos, "FooId", "FooName", Model.FooId), new { #class = "fooSelections" })
Handlers
I call this in the ajax call that adds a record and the change event of any of the select lists. As I stated before, it seems to work fine for any lists that existed when the Partial View was loaded (asp.NET MVC 4) but not for the items added dynamically (their lists are populated but all of the items are enabled). Even when a second dynamic item is added the first one (added in a different action) doesn't have its items disabled.
Question
Anyone see what I am missing? Or is there a better way to accomplish this same thing?
EDIT : Added missing bracket that Robin caught.
I assume you are using razor ajax helper methods to do the ajax call.
In your ajax helper method #Ajax.ActionLink, you can provide a AjaxOptions object in which you can set a JS method to execute on the OnComplete property
Example:
#Ajax.ActionLink("linkText", "actionName", new AjaxOptions {UpdateTargetId = "DomID", OnComplete = "DoTheStuff" })
In case you are not using ajax helpers and using native ajax call or jquery ajax call, just call "DoTheStuff" method in the ajax success callback.
I improved your example and tested it in a fiddle. Please have a look at http://jsfiddle.net/eJudx/
Related
I'm currently looking for a way to add a filter function to a list in SharePoint, which filters the lists content via checkbox selection.
This little mockup might help to understand what I'm talking about:
The idea is to have a normal SharePoint custom list on the right and a checkboxlist on the left side, which will be generated dynamically from the content of the list -> One column is a "filter group" and all the field contents are the filter values.
When selecting one or multiple values on the left side, the items in the list should instantly be filtered accordingly (ajax, without reloading the page).
For example in the mockup, selecting "New York" and "Blue" would only show the item "Steve's Car".
You now, like a product filter in almost every online shop and so on.
How can that be achieved? Would be awesome if this could be included in my solution package (which includes other stuff in C#/jQuery already).
I'm new to frontend customization in SharePoint, but I'm sure there's a way :-)
Edit: I'm using SP2016 by the way.
Pandora!
As i understand it, your first question is how you can dinamically get values for left side filter. I see there two ways: first way, you can build caml query and get items from list; second way, you can use ctx.ListData, which sharepoint initialize by itself and in ctx.ListData.Row (array of items) process items and get unique field values. Note, that ctx.ListData.Row contains only items loaded in list view on the page.
For list filtration you can concat filter link for hash of the page. Example: "?List={ListID}&View={ViewID}&FilterField1=Color-FilterValue1=Blue". Try to filter columns on list and you will see url modification. ListID and ViewId you can retrieve by ctx.listName and ctx.view.
And then pass it in function MyRefreshPageToEx. List will be filtered.
More list filtration info
function FilterList(){
var tableId = $(".ms-listviewtable").attr("id");
var filterUrl = "?List={ListID}&View={ViewID}&FilterField1=Color-FilterValue1=Blue";
MyRefreshPageToEx(tableId, filterUrl, false);
}
function MyRefreshPageToEx(lvTableID, url, bForceSubmit) {
var tblv = document.getElementById(lvTableID);
var clvp = CLVPFromCtx(tblv);
if (clvp != null && clvp.ctx.IsClientRendering) {
clvp.RefreshPaging(url);
clvp.ctx.queryString = url;
if ((typeof clvp.ctx.operationType == "undefined" || clvp.ctx.operationType == SPListOperationType.Default) && Boolean(clvp.ctx.ListData)) {
var fromPage = clvp.ctx.ListData.FirstRow - 1;
var toPage = Number(GetUrlKeyValue("PageFirstRow", false, url));
if (!isNaN(fromPage) && !isNaN(toPage) && fromPage != toPage)
fromPage < toPage ? (clvp.ctx.operationType = SPListOperationType.PagingRight) : (clvp.ctx.operationType = SPListOperationType.PagingLeft);
}
}
else {
SubmitFormPost(url, bForceSubmit);
}
}
I got a question and I am also accepting to getting downvotes for this because I have not really tried something yet. The problem is I don't know how to name the problem and for what I should look for around the internet.
It's like this, I got a link to an api which (in my case) contains all provinces of china in this format:
{
"result":{
"version":"1.0",
"status":100101,
"msg":"got provinces successfully"
},
"data":[
{"province":"\u9999\u6e2f"},
{"province":"\u5317\u4eac\u5e02"}
and some more. Now I want to make a dropdown select menu which contains all this provinces as dropdown values and if one dropdown is selected it should check another URL which says if the selected province is valid or not (in my case it only can be valid because the user cannot enter something himself)
?action=api&i=getCityForProvince&data=北京市
This would be the url for checking this, if it is successful it shows me the cities of the province in the same format like the code above. With this i want to make another select box which only appears if the first is true. In this select box you then select your city and that's it.
I sadly have absolutely no idea how to start with this problem and for what i should look for to solve this.
I wonder if the fact that it's chinese has anything to do with your problem? I bet, it doesn't. With jquery it's pretty easy to accomplish such tasks. It's like building blocks you need to put together.
Learn how to make ajax calls with JQuery. It's quite easy, also it should process your Json result, making it a object or array. So in the callback, you can build up your select box like described here. Another block is to bind to the change event of the select box, which is doing another Ajax call (you already know that now) using the value from the Select input. And in the result of that callback, you can also check the result json and if the result was successful, you can easily fill up another select box using already known methods now, or change its visiblity according to your results.
I think you will want to learn those things, and was not supposed to get a ready coded solution :)
To make your work easier, I recommend you to use:
a template library
an MVVM framework
The difference between using jQuery directly and an MVVM library, or template library, like handlebars or mustache, is that with jQuery you have to take care of handling the elements, and with the other solutions, you leave this work to the framework.
ANyway, I recommend using knockout over using the template libraries, because:
it includes the templates
it can provide a two-way binding
it can handle events
it can apply classes, modify visibility, enable and disable elements...
Here I add a simple example of what it can do:
// This is the ko part:
// This is the view model using Revealing Module Pattern to build it
var vm = (function(){
// The list of provinces which will be shown when available
var provinces = ko.observableArray([]);
// The province selected in the list
var selectedProvince = ko.observable();
// This is what you'd call when the provinces are loaded using AJAX
var loadProvinces = function(data) {
provinces(data);
};
// This functions will be triggered when the selected province changes...
var updateCities = function() {
console.log("Here you'd update cities");
};
// ... because of this subscription
selectedProvince.subscribe(updateCities);
// return the object with the desired properties:
return {
provinces: provinces,
selectedProvince: selectedProvince,
loadProvinces: loadProvinces,
updateCities: updateCities
};
})();
ko.applyBindings(vm);
// AJAX call simulation:
// the returned data
var data = [
{"province":"\u9999\u6e2f"},
{"province":"\u5317\u4eac\u5e02"}
];
// a time out to load the data (simulate AJAX call)
setTimeout(function() { vm.loadProvinces(data);}, 1000);
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/knockout/3.2.0/knockout-min.js"></script>
<div data-bind="visible: !(provinces().length)">
Please wait while loading provinces
</div>
<div data-bind="visible: provinces().length">
<select data-bind="options: provinces,
optionsText: 'province',
optionsValue: 'province',
value: selectedProvince">
</select>
<p>Selected province: <span data-bind="text: selectedProvince"></span></p>
</div>
As you can see, it handles not only creating the DOM elements, bu also handling events, two way-bindig, detecting changes in values...
You could originally write the HTML for your second checkbox and give it a display: none; property. Then on the JS:
if (firstcheckboxValue === true) {
document.getElementById('secondCheckboxId').style='display: block';
}
You could use display: inline-block; or display: inline; etc, whatever suits your layout better.
Things would drastically get easier if you used jQuery. Since there's no code to start working with, I'll just list out steps I'd go through.
1) Write DOM elements for dropdowns, say #dd_provinces #dd_cities. #dd_cities would be hidden.
2) From $().ready(function(){...}) I'd make the web API call.
3) From result callback of the API call in #2, make the second API call(one to fetch cities of the province).
4) Result callback of the second API callback will populate the DOM element #dd_cities
5) Unhide #dd_cities
Sample code:
HTML
<select id="dd_provinces">
</select>
<select id="dd_cities" style="visibility: hidden">
</select>
JS
$(document).ready(function() {
$.ajax({
url: "/echo/json/",
data: "",
success: function(evt) {
var mData = ["City 1", "City 2", "City 3", "City 4"];
for(var i = 0; i < mData.length; i++){
var optionElem = "<option>" + mData[i] + "</option>";
$("#dd_provinces").append(optionElem);
}
$.ajax({
url: "/echo/json",
data: "",
success: function(evt) {
$("#dd_cities").css("visibility", "visible").animate('5000');
}
});
},
error: function(evt) {
console.log(evt);
}
});
});
I have the following issue with angular and the html select-input:
When the user loads the page my select is empty until the data is loaded and returned from the corresponding service.
In this case the select is populated correctly.
However, the user should be able to filter the model with a click on a button.
When I press the button a new request is send to the REST-API and the response contains the new model for the select.
Unfortunately, the select won't update correctly even when I change it's model
Here is some code to illustrate my problem:
// This happens in my controller
EventService.getAvailableRooms(requestObject).then(function successCallback(response){
// sanatizeRoomTypes is used to generate user-friendly names instead of [1, 2,..]
vm.rooms = DataSanatizer.sanatizeRoomTypes(response.data);
}, function errorCallback(response){
console.log(response)
});
vm.rooms is the model of my select:
<select id="roomSelect" ng-model="eventCtrl.selectedRooms"
ng-options="room.name group by room.type for room in eventCtrl.rooms track by room.id" multiple required>
</select>
In some cases the select duplicates it's model or looses entries. It seems like there is some sort of data binding problem.
Thanks,
Try wrapping your model changes in $scope.apply :
$scope.$apply(function() {
$scope.data.myVar = "Another value";
});
Read here for more info:
http://tutorials.jenkov.com/angularjs/watch-digest-apply.html
You can also use $scope.$watch on the changing $scope variables to update them in your view.
I use ASP.NET MVC and Razor. User needs to populate some form which consists of list of objects. So I pass list of empty objects from controller to view. This is part of my main view:
foreach ( var product in Model.Products )
{
Html.RenderPartial( "ProductPartial", product );
}
In ProductPartial user enters some fields for each product.
User can dynamically add or remove products from list. Removing I solved with jquery live function, and it is fine. But I have problem with adding new products.
I solved it in this way: On plus sign click javascript function calls controller action:
public ActionResult NewProduct()
{
Product product = new Product();
product.UniqueId = Guid.NewGuid();
return PartialView( "ProductPartial", product );
}
I need unique id for every product because I want to be able to access products from jquery by ids.
Adding works correctly, but it is extremly slow, since I go on server for every new product. Is there some good way to make it faster?
Well, instead of asking the server for more of the same HTML, you could use jQuery.clone();
In the example I'm copying the first product in the list and giving it a new id, and then adding the copy to the end of the list.
var newProductHtml = $('.MyProducts')[0].clone();
newProductHtml.attr('id', MyNewId);
newProductHtml.appendTo('.MyProductsContainer');
I'm a newbie to Knockout.js. I implemented the Knockout.js by loading data from ajax source and use foreach loop to create a table of the data. The tutorial i followed is here
http://www.dotnetcurry.com/ShowArticle.aspx?ID=933
My issue here is, due to the nature of my application, I find that the first load is better served from the server side using a grid component and I only want Knockout.js to take care of "Add" row, "Update" a row and "delete" a row.
My question is,
1) how do I replace the "first" load and populate the lookupCollection :ko.observableArray() in the article with the default data in the html table?
2) Related to #1. If the first load, the table layout with data is constructed from the server side, then how do I bind "foreach" to the grid so "add" can be performed on lookupCollection?
Thanks and again, I'm a newbie, I must be missing some key concepts here.
One way would be to pass your initial data into your view model. Since you are using asp.net it would look something like this:
//Dump raw data into javascript variable
var data = #Html.Raw(ViewBag.Data);
function ViewModel(data) {
var self = this;
//Unpack raw data
self.lookupCollection = ko.observableArray(data.lookupCollection);
}
//initialize view model
var viewModel = new ViewModel(data);
ko.applyBindings(viewModel);