How can I change the value of an item in an array based on multiple conditions? - javascript

I am trying to change the value of an item in array, based on matching other items in the array. The array might contain details of the section (non-unique), a unique ID, and a value I wish to change (in this case a 'selected' flag). The goal is to be able to have multiple items which can have their selected flag. Within any single section, only one item could be 'selected' but multiple sections could have an individual item 'selected'. Conceptually, I think this could be thought of in the same way as having multiple groups of radio buttons.
The ultimate aim is to be able to use state to remember the selections made in a component that is created using props. I'm keen to understand not simply copy. I'll get my head around state mutations next, but better to solve this problem first.
So, take an array like:
menuItems: [
{
section: 'National',
id: 'First item',
selected: false
},
{
section: 'National',
id: 'Second item',
selected: false
},
{
section: 'National',
id: 'Third item',
selected: true
},
{
section: 'Local',
id: 'Fourth item',
selected: false
},
{
section: 'Local',
id: 'Fifth item',
selected: false
},
{
section: 'Local',
id: 'Sixth item',
selected: true
}
]
And some search strings like:
searchSection: 'National',
searchId: 'First item'
How would I create a function that could change the selected flag of the item with id: First item to true, the others (second, third item) to false, and don't change anything in the 'Local' section?
I have tried to get my head around using forEach loops to no avail, even though this feels the right approach. Using findIndex for the section seems destined to fail as there are multiple items to be found.
First SO question - so pre-emptive apologies if problems with the way I have asked. I'm using Vue3. All advice appreciated.

Loop through the items testing for the proper section. With the section, if there is an id match, set selected to true, otherwise set selected to false:
methods: {
flag(searchSection, searchId) {
this.menuItems.forEach(item => {
if (item.section === searchSection) {
item.selected = item.id === searchId;
}
});
}
}
Call the function:
this.flag('National', 'First item');

Related

Getting the selected items count in a kendoMultiSelect footerTemplate

Is it possible to get the selected items count in the kendoMultiSelect's footerTemplate?
I created a DOJO example with an attemp to use instance.dataItems().length but for some reason, the value is always 0.
$("#customers").kendoMultiSelect({
dataSource: [
{ id: 1, name: "Apples" },
{ id: 2, name: "Oranges" }
],
dataTextField: "name",
dataValueField: "id",
footerTemplate: '#: instance.dataItems().length # item(s) selected'
});
EDIT:
due #Aleksandar comment where he points out
Calling setOptions in an event handler or the respective widget is not
recommended and can cause an endless loop or a JavaScript error.
I take his suggestion into account and add his solution as an answer.
footerTemplate: '<span id="total">#:instance.value().length#</span> item(s) selected',
change:function(e){
var itmsSelected = e.sender.value().length;
$("#total").html(itmsSelected);
}
OBSOLETE:
Guess it's not in an observable object. One of the possible solutions is to change footerTemplate
every time a change happens on multiSelect:
var multi = $("#customers").kendoMultiSelect({
dataSource: [
{ id: 1, name: "Apples" },
{ id: 2, name: "Oranges" }
],
change: function() {
this.setOptions({"footerTemplate": this.value().length +" item(s) selected"});
},
dataTextField: "name",
dataValueField: "id",
footerTemplate: '0 item(s) selected'
}).getKendoMultiSelect();
Example: Footer template update

Curious case of "Unexpected closing tag" in modern Angular [closed]

Closed. This question needs to be more focused. It is not currently accepting answers.
Want to improve this question? Update the question so it focuses on one problem only by editing this post.
Closed 2 years ago.
Improve this question
Suppose we have a data model, a list of objects that represent text items to be rendered in template. Each carry a text and a type. A type could be HEADER, PARAGRAPH and LIST. Each would correspond to a similar HTML tag, so HEADER would be rendered by <h1>, PARAGRAPH would be <p> and LIST would be a <li> eventually wrapped in an <ul></ul>. We have a given set of these objects. For example:
[
{ textType: 'HEADER', text: 'Welcome!' },
{ textType: 'PARAGRAPH', text: 'Lorem ipsum is a lie, here are the facts:' },
{ textType: 'LIST', text: 'List item #1' },
{ textType: 'LIST', text: 'List item #2' },
{ textType: 'LIST', text: 'List item #3' },
{ textType: 'HEADER', text: 'Welcome again!' },
{ textType: 'PARAGRAPH', text: 'Lorem ipsum is not a lie after all, counterpoints:' },
{ textType: 'LIST', text: 'Another List item #1' },
{ textType: 'LIST', text: 'Another List item #2' },
{ textType: 'LIST', text: 'Another List item #3' },
{ textType: 'PARAGRAPH', text: 'That\'s all, folks!' }
]
The problem is quite obvious - both HEADER AND PARAGRAPH can be expressed by a one closed tag with HTML content. But LIST not only requires a standard <li> element, but also a <ul> (if we assume want strict HTML). How to render this within Angular template? I went about a most natural approach I could think of - *ngFor with some additional, unusual albeit reasonable logic added. In a way, it looks like an old-school PHP-style render.
<ng-container *ngFor="let textItem of summaryText; let index = index">
<h1 *ngIf="textItem.textType === 'HEADER'">{{ textItem.text }}</h1>
<p *ngIf="textItem.textType === 'PARAGRAPH'">{{ textItem.text }}</p>
<ng-container *ngIf="textItem.textType ==='LIST'">
<ng-container *ngIf="(index === 0) || (summaryText[index-1] && (summaryText[index-1].textType !== 'LIST'))">
<ul>
</ng-container>
<li>{{ textItem.text }}</li>
<ng-container *ngIf="(index === (summaryText.length - 1)) || (summaryText[index+1] && (summaryText[index+1].textType !== 'LIST'))">
</ul>
</ng-container>
</ng-container>
</ng-container>
I was somewhat surprised to learn, that this wouldn't compile. It results with Unexpected closing tag "ng-container". And, sure enough, when you think about it, that makes sense. Angular enforces proper HTML so it expects the <ul> closure within a directive, when there's none. I understand that well enough.
But that still leaves me with an unsolved problem - how to render the list wrapped in <ul>, when I don't know when it will happen in that initial model? I cannot assume <ul> anywhere. I cannot even assume the order of the items in that model - it might as well just be a PARAGRAPH, LIST, PARAGRAPH, LIST and what not (it would result in two lists with single item each).
Is there any way to achieve that without influencing the data model? I know I could theoretically change the model, make a separate list inside, detect that, start ul and so on. But in this case, for some reason (please assume it's reasonable and that I know what I'm talking about) I cannot change the model.
I was wondering if there's any way to make Angular render that properly. Or, if this is - perhaps - a wrong approach, there is another way to tackle this directly in the template?
I created a bug-fixed version here for you on Stackblitz.
The error is because of that the HTML Nested tag rules are violated here in your template:
<ng-container *ngIf="(index === 0) || (summaryText[index-1] && (summaryText[index-1].textType !== 'LIST'))">
<ul>
</ng-container>
You have a closing tag </ng-container> before closing inner tag <ul>.
In addition You have to change the JSON object data for have better structure of <li> items like this:
this.summaryText = [
{ textType: 'HEADER', text: 'Welcome!' },
{ textType: 'PARAGRAPH', text: 'Lorem ipsum is a lie, here are the facts:' },
{ textType: 'LIST', text: ['List item #1', 'List item #2', 'List item #3'] },
{ textType: 'HEADER', text: 'Welcome again!' },
{ textType: 'PARAGRAPH', text: 'Lorem ipsum is not a lie after all, counterpoints:' },
{ textType: 'LIST', text: ['Another List item #1', 'Another List item #2', 'Another List item #3'] },
{ textType: 'PARAGRAPH', text: 'That\'s all, folks!' }
]

Angular using filter to Update?

I wanted to update my model using $filter or some other smart way without having to do multiple for each.
So basically I have my model similar to below:
$scope.projects = [
{
tasks: [
{
name: 'task name',
visible: true,
starred: true
}
],
createdAt: 'something'
},
{
tasks: [
{
name: 'second task name',
visible: true,
starred: false
}
],
createdAt: 'something'
}
]
What I wanted to do is by using $filter or some other way like underscore and so on, to update the content of the variable. So for instance, when I click a button, I'd like to set visible = true only to tasks that are starred.
Anyone have a suggestion on how to achieve that? Is it possible or I would have to do a couple of loops?
Something like:
$filter('filter')($scope.projects, {{starred = true}}).tasks.visible = true
UPDATE
With the help from #jbrown I was able to achieve what I wanted.
Just in case someone needs similar approach, the final solution was as written below:
_.forEach($scope.projectsModel.projects, function(proj){
_.forEach(_.filter(proj.tasks, {starred: true}), function(task){
task.visible = true;
});
});
Using underscore you can use a combination of filter and find to get the results you are looking for.
$scope.filteredProjects = _.filter($scope.projects, function(proj) {
return _.find(proj.tasks, {
visible: true, starred: true });
});

Getting the result suggestion list in jquery autocomplete

I'm using the jquery autocomplete plugin and I am comming up to a few problems:
I have a lot of data and when I type data a long suggestion list is shown and a scrollbar is needed:
$("#txtName").autocomplete(data,
{ matchContains: true,
minChars: 0,
max: 3000,
scroll: true,
//scrollHeight: 180,
width: 200
});
but, the scrollbar does't work properly in IE (it's a known issue, I searched alot but have'nt found a relevant solution).
so I decided to block the suggestion list popup and get the suggestion list results into an array or somthing similar and show them in my control.
my problem is - How do I get that list?
Thanks in advance!
Quickly looking through that Plugin's API, I don't see any events that let you handle the response from a server call-back. You may want to switch and use the official JQuery UI library for your auto-completing needs. There is an appendTo option that might suit your need.
I found the answer (part of it, I still need to work on it).
I'll first post the code and then explain it:
$(function ()
{
var names = [
{ label: 'Java', value: '1' },
{ label: 'C++', value: '2' },
{ label: 'C#', value: '3' },
{ label: 'Jquery', value: '4' },
{ label: 'Javascript', value: '5' },
{ label: 'ASP', value: '6' },
{ label: 'Pearl', value: '7' },
{ label: 'VB', value: '8' },
{ label: 'Ajax', value: '9' },
{ label: 'Json', value: '10' }];
$("#txtName").autocomplete({
minLength: 2,
source: names,
delay: 500
}).data("autocomplete")._renderItem = function (ul, item)
{
//add data to my control, need to take care of earasing each time.
var elOptNew = document.createElement('option');
elOptNew.text = item.label;
elOptNew.value = item.value;
lst.add(elOptNew);
//this code here adds the items to the popup thats built in.(it's written in jquery-ui.min.js)
return $("<li></li>")
.data("item.autocomplete", item)
.append("<a>" + item.label + "</a>")
.appendTo(ul);
};
});
Html:
<input id="txtName"/>
<select id="lst" size='10'></select>
The added part (_renderItem) adds one item each time, so you can do whatever you want to do with an item. I decided to add it to a list.
The other thing that's not done is erasing the list each time. I still need to figure out how to do that.

Ext-JS: How to disable cell editing for individual cells in a grid?

I am now building a web application with Ext-JS 4.0.2, and I am using a editable grid to control the data to be shown for a table on the same page.
To make the grid editable, I followed the API documentation and used the following:
selType: 'cellmodel',
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 2
})
]
However, for this grid, there are several cells that are not supposed to be changed.
I could simply let the event handler change the data back to the right state once it is changed in the grid, but this seems to be hacky, hard to maintain, and unreadable. Is there any better way to do this? I read the API but cannot find any useful attributes.
UPDATE
As for this particular app, just disable the first row would work. But I am also interested in choose several grid and make them not editable (imagine a Sudoku game with a grid).
As I've understand from comments you want to make first row not editable. There is ugly but quick solution. Assign to your plugin beforeedit handler. And when event is being fired check what row is being edited. If first - return false:
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 2,
listeners: {
beforeedit: function(e, editor){
if (e.rowIdx == 0)
return false;
}
}
})
]
Check out docs for beforeedit.
UPDATE
Docs say that beforeedit has such set of params:
beforeedit( Ext.grid.plugin.Editing editor, Object e, Object options )
But there is mistake. The correct sequance is:
beforeedit( Object e, Ext.grid.plugin.Editing editor, Object options )
I've updated example due to this fact.
You can specify ColumnModel to declare editable and not editable columns:
var cm = new Ext.grid.ColumnModel({
columns: [{
dataIndex: 'id',
header: 'id',
hidden: true
},{
dataIndex: '1',
header: '1',
editor: new Ext.form.TextField({})
},{
dataIndex: '2',
header: '2',
editor: new Ext.form.NumberField({})
},{
dataIndex: '3',
header: '3'
}]
});
var grid = new Ext.grid.EditorGridPanel({
store: store,
clicksToEdit: 2,
cm: cm
...
In this example column id is unvisible, columns 1 and 2 editable (with text and number editors) and column 3 is not editable.
UPDATE:
Prevent row editing:
grid.on('beforeedit', function(event) {
if (event.row == 0) {
this.store.rejectChanges();
event.cancel = true;
}
}, grid);
As Ziyao Wei mentioned the documentation for the beforeEdit event is wrong. However you need to reference the 'editor' parameter to get the row index and other values, not the first object parameter 'e'.
Updated example:
plugins: [
Ext.create('Ext.grid.plugin.CellEditing', {
clicksToEdit: 2,
listeners: {
beforeedit: function(e, editor){
if (editor.rowIdx == 0)
return false;
}
}
})
]

Categories

Resources