I have a dynamic form where the user may add as many ComboBox as they need. All ComboBoxes contain the same predefined list of values. Is it possible somehow with the binding and validation mechanism to prevent the user from selecting the same value in two or more ComboBoxes? Show validation error in such case.
UPDATED
ComboBox<CompositeEntityResult> locationComboBox = new ComboBox<>("Location");
locationComboBox.setRequiredIndicatorVisible(true);
if (focus) {
locationComboBox.focus();
}
locationComboBox.setItems(query -> {
String searchQuery = createComboBoxSearchQuery(locationComboBox, query);
VaadinUtils.invalidateQueryOffsetAndLimit(query);
return locationService.findByNamePatternSorted(searchQuery, alreadyAddedLocationIds, alreadyAddedLocationUnknownNames, VaadinUtils.getCurrentLocaleIso6391(), AUTOCOMPLETE_PAG, AUTOCOMPLETE_PAGE_SIZE).stream();
});
locationComboBox.setAllowCustomValue(true);
locationComboBox.addCustomValueSetListener(e -> {
locationComboBox.setValue(new CompositeEntityResult(new Skill(e.getDetail(), null)));
});
locationComboBox.setItemLabelGenerator(e -> TranslationUtils.getTranslatedName(e.getNode()));
locationComboBox.setRenderer(createCompositeEntityResultComboBoxRenderer(locationComboBox));
locationComboBox.addValueChangeListener(v -> {
addAlreadyAddedIdAndName(v.getValue(), alreadyAddedLocationIds, alreadyAddedLocationUnknownNames);
});
locationComboBox.getStyle().set("--vaadin-combo-box-overlay-width", "48em");
binder.forField(locationComboBox)
.asRequired("Specify location")
.bind(dto -> locationCompositeEntityResult, (dto, v) -> {
if (v != null) {
Long locationId = v.getNode().getId();
if (locationId != null) {
dto.addAddedLocationId(locationId);
} else {
dto.addUnknownLocation(v.getNode().getName());
}
}
});
I partially solved the issue and collect IDs in addValueChangeListener, then filter them on the database level. But the issue is that I need to allow user custom input as well. So I don't know how to prevent the same custom values right now.
Sounds like a job for bean level validation
#Test
public void beanLevelValidation() {
final Bean bean = new Bean();
final Binder<Bean> binder = new Binder<>(Bean.class);
binder.setBean(bean);
binder.withValidator(item ->
item.getLocations().stream().distinct().count() == item.getLocations().size(),
"Items have to be unique"
);
ComboBox<Object> locationComboBox = new ComboBox<>("Location");
binder.forField(locationComboBox)
.bind(
compositeEntityResult -> null,
(item, location) -> item.getLocations().add(location)
);
locationComboBox.setValue("sameValue");
locationComboBox.setValue("differentValue");
locationComboBox.setValue("sameValue");
final BinderValidationStatus<Bean> validationStatus = binder.validate();
assertThat(validationStatus.getValidationErrors().stream()
.map(ValidationResult::getErrorMessage)
.collect(Collectors.toList()), hasItem("Items have to be unique")
);
}
Related
how I can update a single value for an already existing row in the db by only having a parameters that I want to add it to this attribute
here is my code for a trivial way but didnt work
public bool BuyBook(int BookId, int UserId, int BookPrice){
using (var ctx = new OnlineBooksEntities())
{
User updatedCustomer = (from c in ctx.Users
where c.UserId == UserId
select c).FirstOrDefault();
updatedCustomer.Balance = BookPrice;
ctx.SaveChanges();
}
this.DeleteBook(BookId);
return true;
}
Add an sql query to the method solves the update aim
public bool BuyBook(int BookId, int UserId, int BookPrice)
{
try
{
using (var ctx = new OnlineBooksEntities())
{
User user = ctx.Users.Where(x => x.UserId == UserId).FirstOrDefault();
BookPrice = (int)user.Balance + BookPrice;
int noOfRowUpdated =
ctx.Database.ExecuteSqlCommand("Update Users set Balance = "+BookPrice+ " where UserId ="+UserId);
}
Updating basically means changing an existing row's value. Since you mentioned EF, you can do this by retrieving the object, changing its value, and saving it back. Thus you can do something like this:
using (var db = new MyContextDB())
{
var result = db.Books.SingleOrDefault(b => b.BookPrice == bookPrice);
if (result != null)
{
result.SomeValue = "Your new value here";
db.SaveChanges();
}
}
I Create A News Site With MVC5 But I Have Problem .
in Model i Create A Repository Folder And in this i Create Rep_Setting for
Connect to Tbl_Setting in DataBase .
public class Rep_Setting
{
DataBase db = new DataBase();
public Tbl_Setting Tools()
{
try
{
var qGetSetting = (from a in db.Tbl_Setting
select a).FirstOrDefault();
return qGetSetting;
}
catch (Exception)
{
return null;
}
}
}
And i Create a Rep_News for Main Page .
DataBase db = new DataBase();
Rep_Setting RSetting = new Rep_Setting();
public List<Tbl_News> GetNews()
{
try
{
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals("News")
select a).OrderByDescending(s => s.ID).Skip(0).Take(RSetting.Tools().CountNewsInPage).ToList();
return qGetNews;
}
catch (Exception ex)
{
return null;
}
}
But This Code Have Error to Me
OrderByDescending(s=>s.ID).Skip(0).Take(RSetting.Tools().CountNewsInPage).ToList();
Error :
Error 18 'System.Linq.IQueryable<NewsSite.Models.Domain.Tbl_News>' does
not contain a definition for 'Take' and the best extension method overload
'System.Linq.Queryable.Take<TSource>(System.Linq.IQueryable<TSource>, int)' has
some invalid arguments
E:\MyProject\NewsSite\NewsSite\Models\Repository\Rep_News.cs 50 52 NewsSite
How i Resolve it ?
Try it this way. The plan of debugging is to split your execution, this also makes for a more reusable method in many cases. And a good idea is to avoid using null and nullables if you can, if you use them "on purpose" the you must have a plan for them.
DataBase db = new DataBase();
Rep_Setting RSetting = new Rep_Setting();
public List<Tbl_News> GetNews()
{
int skip = 0;
Tbl_Setting tools = RSetting.Tools();
if(tools == null){ throw new Exception("Found no rows in the database table Tbl_Setting"); }
int? take = tools.CountNewsInPage;//Nullable
if(!take.HasValue)
{
// Do you want to do something if its null maybe set it to 0 and not null
take = 0;
}
string typeStr = "News";
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals(typeStr)
select a).OrderByDescending(s => s.ID).Skip(skip).Take(take.Value);
return qGetNews.ToList();
}
if qGetNews is a empty list you now don't break everything after trying to iterate on it, like your return null would. instead if returning null for a lit return a new List<>() instead, gives you a more resilient result.
So I said reusable method, its more like a single action. So you work it around to this. Now you have something really reusable.
public List<Tbl_News> GetNews(string typeStr, int take, int skip = 0)
{
List<Tbl_News> qGetNews = (from a in db.Tbl_News
where a.Type.Equals(typeStr)
select a).OrderByDescending(s => s.ID).Skip(skip).Take(take);
return qGetNews.ToList();
}
Infact you shjould always try to avoid returning null if you can.
public class Rep_Setting
{
DataBase db = new DataBase();
public Tbl_Setting Tools()
{
var qGetSetting = (from a in db.Tbl_Setting
select a).FirstOrDefault();
if(qGetSetting == null){ throw new Exception("Found no rows in the database table Tbl_Setting"); }
return qGetSetting;
}
}
I'm trying to set field at index 0 in Vaadin combo box to default value, so I could avoid error message if user doesen't select anything. So I would like that instead of blank field I have populated field at index 0.
I have tried to set it and managed it with this:
field.setNullSelectionAllowed(true);
field.setNullSelectionItemId(container.getIdByIndex(0));
So I don't have blank value at index 0, instead my previous value of index 1 is now at index 0. And that is exactly what I want and need and in combo box looks just as I want.
But, unfortunately, when I submit my form, value is not passed. Only values after index 0 are passed. It's so frustrating, can somebody help me? Value passed to setNullSelectionItemId exists 100%.
How can I grab value from index at place 0 in combo box?
p.s. here is my code:
public Field<?> buildAndBindComboBox(final String caption, final BeanItemContainer<?> container,
final Object propertyId, final String title, final ValueChangeListener listener, final boolean nullAllowed,
final boolean required, final boolean enabled, final boolean visible) {
#SuppressWarnings("serial")
ComboBox field = new ComboBox(caption, container) {
// http://dev.vaadin.com/ticket/10544
// - typing in ComboBox causes Internal Error
private boolean inFilterMode;
#Override
public void containerItemSetChange(com.vaadin.data.Container.ItemSetChangeEvent event) {
if (inFilterMode) {
super.containerItemSetChange(event);
}
}
#Override
protected List<?> getOptionsWithFilter(boolean needNullSelectOption) {
try {
inFilterMode = true;
return super.getOptionsWithFilter(needNullSelectOption);
} finally {
inFilterMode = false;
}
}
};
field.setStyleName("comboBox");
field.setInputPrompt("Select");
if(defaultValue == true){
field.setNullSelectionAllowed(false);
field.setNullSelectionItemId(container.getIdByIndex(0).toString());
//field.select(container.getIdByIndex(0));
//field.setValue(container.getIdByIndex(0));
//field.setRequired(false);
defaultValue = false;
} else {
field.setNullSelectionAllowed(nullAllowed);
field.setRequired(required);
}
field.setImmediate(true);
field.setNewItemsAllowed(false);
field.setFilteringMode(FilteringMode.CONTAINS);
if (title != null) {
field.setItemCaptionPropertyId(title);
}
//field.setNullSelectionAllowed(nullAllowed);
//field.setRequired(required);
field.setVisible(visible);
if (listener != null) {
field.addValueChangeListener(listener);
}
this.bind(field, propertyId);
field.setEnabled(enabled);
return field;
}
public void setDefaultValueFirstItem(boolean def){
defaultValue = def;
}
It is binded like this:
commitmentFeeBinder.setDefaultValueFirstItem(true);
commitmentFeeBinder.buildAndBindComboBox("No working day labels", noWorkingDays, "noWorkingDaysCF", "title", null, false, !transaCF, true, !transaCF);
If I understood your question correctly, Steffen Harbich is correct in suggesting that if you want the first item to be selected by default you should disable null selection and select the first item by default. E.g. this works:
ComboBox cb = new ComboBox("", Arrays.asList("First", "Second", "Third"));
cb.setNullSelectionAllowed(false);
cb.select("First");
Or alternatively with a BeanItemContainer:
List<MyBean> beans = Arrays.asList(new MyBean("First"), new MyBean("Second"), new MyBean("Third"));
BeanItemContainer<MyBean> bic = new BeanItemContainer<>(MyBean.class, beans);
ComboBox cb = new ComboBox("", bic);
cb.setNullSelectionAllowed(false);
cb.select(bic.getIdByIndex(0));
private void resetComboBoxToIndex(ComboBox combo, int index) {
BeanItemContainer<Bean_ComboBox> items_combo = (BeanItemContainer<Bean_ComboBox>)combo.getContainerDataSource();
if(items_combo != null && items_combo.size() > index) {
Bean_ComboBox primerItem = items_combo.getIdByIndex(index);
if(primerItem != null) {
combo.select(primerItem);
}
}
}
I am implementing a simple app, where in the registration page user can select news categories. Requirements are below
All the categories are the CheckBoxField's. User have to select at least one category.
Select all CheckBox will allow to select all/deselect all categories CheckBox.
If user manually selects all checkbox fields then "Select All" checkbox must be selected.
Approaches: I have created the categories checkbox in a loop.
for(int i=0;i<interests.length;i++){
allFields[i] = new ColorCheckBoxField(interests[i], false, checkBoxStyle | USE_ALL_WIDTH);
allFields[i].setCookie(i+"");
allFields[i].setFont(bodyFont);
allFields[i].setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
ColorCheckBoxField tempChoice = (ColorCheckBoxField)field;
int index =Integer.parseInt(tempChoice.getCookie().toString().trim());
//set the selection
if(tempChoice.getChecked()){
parent.selectInterest(index);
}
boolean flag = true;
int[] intrests = parent.getSelectedInterest();
for (int i = 0; i < intrests.length; i++) {
if(intrests[i]==0){
flag = false;
}
}
if(flag==true){
selectAll.setChecked(flag); // select all is Checkbox object
}else{
selectAll.setChecked(false);
}
}
});
vfm.add(allFields[i]);
}
My selectAll checkbox logic is
selectAll = new ColorCheckBoxField("Select All", false, checkBoxStyle | USE_ALL_WIDTH);
selectAll.setChangeListener(new FieldChangeListener() {
public void fieldChanged(Field field, int context) {
ColorCheckBoxField temp = (ColorCheckBoxField) field;
//if (context == FieldChangeListener.PROGRAMMATIC ) {
checkAll(temp.getChecked()); // it loops through all checkbox and set them checked
//}
}
});
innerHfm.add(selectAll);
I understand the problem, its due to infinite loop. I have used "FieldChangeListener.PROGRAMMATIC" but that wont help because i want the field listener to work for both pragmatically and manually. I don't have any option left to fix. Any hack will help me?
That's correct that you have to use FieldChangeListener.PROGRAMMATIC. But you have to use it with interest checkboxes instead of using it for selectAll checkbox.
Please add one defensive check to FieldChangeListener for interest checkboxes:
if ( nonProgrammaticChange(context) ) {
ColorCheckBoxField tempChoice = (ColorCheckBoxField)field;
int index = Integer.parseInt(tempChoice.getCookie().toString().trim());
...
}
Where nonProgrammaticChange is:
private boolean nonProgrammaticChange (int context) {
return (context & FieldChangeListener.PROGRAMMATIC) != FieldChangeListener.PROGRAMMATIC;
}
I see bug in your code - you don't clear interest in parent if checkbox is unchecked.
Minor improvements as for me - use Vector where you'll store indexes of selected checkboxes. This will allow to replace this code:
boolean flag = true;
int[] intrests = parent.getSelectedInterest();
for ( int i = 0; i < intrests.length; i++ ) {
if( intrests[i] == 0 ) {
flag = false;
}
}
To this code:
selectedInterestIndexes.size() == interests.length
And probably this will give you less iteration in other places.
As well I would work more on removal of duplicates and code readability.
I want to create a method which can takes the properties I possibly may update and leaving those not interested untouched.
Here is what I did:
public static void updateTable(int id, string field1, string field2, string field3){
using(var context = new Entities()){
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
if(obj != null){
obj.field1 = field1;
...
obj.SaveChanges();
}
}
}
But in this pattern, I need to pass all 4 parameters into the method even I just want to update only one field. Is there any generic solution to update only the fields I passed in?
I came up something like this:
public static void updateTable(int id, object data_json){
using(var context = new Entities()){
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
if(obj != null){
if(data_json['field1']!=null) //something like this
obj.field1 = data_json['field1'];
...
obj.SaveChanges();
}
}
}
But this can't handle the case that I do want to set a field to be null. Or is there any better solution?
If you don't care about updating relationships, you can use ApplyCurrentValues, which only updates the scalar properties.
E.g:
public static void updateTable(int id, object data_json){
using(var context = new Entities()) {
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
context.ApplyCurrentValues("Table", data_json);
}
}
It assumes an entity with the same key is already attached in the graph. In this case, the query for var obj will ensure the object is in the graph, then it's contents are overridden with the scalar properties on the supplied object.
You might need an explicit cast on data_json to ensure it is of the same type contained in the entity set.
Using an ExpandoObject would allow you to send in only the properties you want to set, and would allow you to specify null values as well.
For example:
public static void updateTable(int id, dynamic data){
using(var context = new Entities()){
var obj = context.Table.Where(x=>x.id == id).FirstOrDefault();
if(obj != null){
if (((IDictionary<string, object>)data).ContainsKey("field1"))
obj.field1 = data.field1;
...
obj.SaveChanges();
}
}
}
and you could call it like this:
dynamic data = new ExpandoObject();
data.field1 = 123;
data.field2 = null;
data.field5 = "abc";
MyClass.updateTable(1, data);
Everything can be solved with a moment of reflection. This function solves the problem:
public void UpdateTable(int id, object values)
{
using (var entities = new MyEntities())
{
var valuesType = values.GetType();
var element = entities.MyTable.Where(t => t.ID == id).First();
//We are iterating through all properties of updated element and checking
//if there is value provided for there properties in values parameter
foreach (var property in element.GetType().GetProperties())
{
var valuesProperty = valuesType.GetProperty(property.Name);
//If values contain this property
if (valuesProperty != null)
{
//taking value out of values parameter
var value = valuesProperty.GetValue(values, null);
//setting it in our element to update
property.SetValue(element, value, null);
}
}
entities.SaveChanges();
}
}
Usage:
UpdateTable(125, new { FieldA = 1, FieldB = "ABCD" });
You can even make this method more universal by adding generic table type parameter.