Problem with ui-select with multiple and async options - javascript

I have a problem with the ui-select directive (AngularJS 1.6.4, Ui-select 0.19.8).
I have created the fiddle here.
It's supposed to show contacts in the dropdown I if type more than 3 chars. (I don't even try to filter anything at the moment, the same list of contacts is returned). It works well the first time, then the other times no dropdown is shown.
I use the async way of returning results so it calls my "refresh" function after 1sec.
Can someone help me understand why no dropdown is displayed after the first time ?
(also, if someone knows why I need to force the display: block for the <ul> tag - cf CSS )
Thanks
HTML
<body ng-app="myApp">
<div body ng-controller="ctrl as ctrl">
<div class="element">
Selected Contacts:<br>
<div ng-repeat="contact in ctrl.contacts" class="contact">
{{ contact }}
</div>
</div>
<ui-select multiple ng-model="ctrl.contacts" class="element">
<ui-select-match placeholder="Pick one...">{{$item}}</ui-select-match>
<ui-select-choices
position="down"
refresh="ctrl.refreshContacts($select.search)"
refresh-delay="1000"
minimum-input-length="3"
repeat="person in ctrl.people">
<div ng-bind-html="person | highlight: $select.search"></div>
</ui-select-choices>
</ui-select>
<div class="element">
<div ng-repeat="log in ctrl.logs track by $index" >
<div>
{{log}}
</div>
</div>
</div>
</div>
</body>
JS
var myApp = angular.module('myApp', ['ngSanitize','ui.select']);
myApp.controller("ctrl", [function () {
var ctrl = this;
ctrl.logs=[];
ctrl.refreshContacts = function(search) {
var people = [
"mickael",
"pierre",
"anna",
"alice",
"bob"
];
ctrl.people = people;
ctrl.logs.push("refreshContacts called")
}
ctrl.people = [];
}]);
CSS
.element {
margin-bottom: 20px;
}
.contact {
display: inline-block;
margin: 5px;
padding: 5px 8px;
background: grey;
}
/* why do I need this ?! */
.ui-select-choices {
display: block;
}

If I am remembering correctly there is a bug when using both minimum-input-length and refresh. I solved this problem by removing minimum-input-length and adding an if statement inside refresh function.
ctrl.refreshContacts = function(search) {
if(search == undefined || search.length < 3){
return;
}
else{
people = [
"mickael",
"pierre",
"anna",
"alice",
"bob"
];
ctrl.people = people;
}
}

Related

Apply class based on integer value in an AngularJS expression

I'm calculating differences between 2 values then storing that value in a variable named "allCallsNet", I want to toggle a class based on if the number is a Positive or Negative number in my HTML, using AngularJS Expressions
Example:
{allCallsNet=allCallsWeek1 - allCallsWeek2;""}
<p ng-class"{{positive: (allCallsNet.value != 'Negative')}}">{{allCallsNet}}</p>
And if you also need help checking the number sign this could help
angular.module('Stack', []).controller('MainCtrl', function($scope) {
$scope.allCallsNet = 0;
$scope.add = function() {
$scope.allCallsNet += 1;
}
$scope.sub = function() {
$scope.allCallsNet -= 1;
}
});
.negative {
color: red;
}
.positive {
color: green;
}
<script src="https://cdnjs.cloudflare.com/ajax/libs/angular.js/1.7.5/angular.min.js"></script>
<div ng-app="Stack" ng-controller="MainCtrl">
<!-- You can use the ternary operator, to exchange between classes -->
<p ng-class="allCallsNet > 0 ? 'positive' : 'negative'">Start editing and see your changes reflected here!</p>
<button ng-click="add()">Add</button>
<button ng-click="sub()">Subtract</button>
<p>{{ allCallsNet }}</p>
</div>
Try this:
<p [class.positive]="allCallsNet.value != 'Negative'" >{{allCallsNet}}</p>

Flow data between 2 tabs of the same page

I opened the same page in different tabs with different mode and when I pressed one of the button that should fill the inputs, the data filled in the last tab opened, not the tab that has been clicked.
The angular input:
<input type="text"
class="form-control"
id="Type"
style="width:170px;text-align:auto"
ng-model="This_Page.Data.Type"
ng-disabled="This_Page.Action == 'View'"
autocomplete="off">
code:
$scope.This_Page.Data.Type = l_Details.Type;
what should I do to ensure that every tab get only it's data?
EDIT 1:
I guess my question was not clear enough...
I have an application that is built based on TABs, each with its own controller assigned, that open upon request. Some of the tabs serve for dual behavior (same HTML, same Controller code): Create an element (e.g. User Login credentials) and Edit an element (Edit User Credentials).
The issue is that when I open two instances of the same tab, whatever I enter in one of the tabs leaks to the other instance of the same tab.
Hope this makes things clearer.
Thanks!
Use this code:
You can bind with : {{}} and in js return html with : ${}
(function () {
'use strict';
angular
.module('MyApp',['ngMaterial'])
.controller('AppCtrl', AppCtrl);
function AppCtrl ( $scope ) {
$scope.data = {
selectedIndex: 0,
firstDataSet: "First Data set",
secondDataSet: "Second Data set"
};
$scope.selectedTab = function(index) {
if(index === 'one')
return $scope.data.firstDataSet;
if(index === 'two')
return $scope.data.secondDataSet;
}
}
})();
.tabsdemoStaticTabs md-tab-content {
padding: 25px; }
.tabsdemoStaticTabs md-tab-content:nth-child(1) {
background-color: #42A5F5; }
.tabsdemoStaticTabs md-tab-content:nth-child(2) {
background-color: #689F38; }
.tabsdemoStaticTabs md-tab-content:nth-child(3) {
background-color: #26C6DA; }
.tabsdemoStaticTabs .after-tabs-area > span {
margin-top: 25px;
padding-right: 15px;
vertical-align: middle;
line-height: 30px;
height: 35px; }
.tabsdemoStaticTabs .after-tabs-area > md-checkbox {
margin-top: 26px;
margin-left: 0; }
<link rel="stylesheet" href="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.8/angular-material.min.css">
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-animate.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-aria.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angularjs/1.6.9/angular-messages.min.js"></script>
<script src="https://ajax.googleapis.com/ajax/libs/angular_material/1.1.8/angular-material.min.js"></script>
<div ng-app="MyApp" class="tabsdemoStaticTabs" ng-controller="AppCtrl" ng-cloak="">
<md-content class="md-padding">
<md-tabs class="md-accent" md-selected="data.selectedIndex">
<md-tab id="1" md-on-select="resultOne = selectedTab('one')">
<md-tab-label>
<h3>My Tab content</h3>
</md-tab-label>
<md-tab-body>
<p>
{{ resultOne }}
</p>
</md-tab-body>
</md-tab>
<md-tab id="2" md-on-select="resultTwo = selectedTab('two')">
<md-tab-label>
<h3>My Tab content</h3>
</md-tab-label>
<md-tab-body>
<p>
{{ resultTwo }}
</p>
</md-tab-body>
</md-tab>
</md-tabs>
</md-content>
</div>

Filter elements in DOM based on data-attr with jQuery

I'm trying to filter these items with jQuery autocomplete according to their data-name, but I got stuck with it a bit. Generally, I want to start typing the text in the input field and remove items from DOM if they don't match. Any help is much appreciated.
Pen: https://codepen.io/anon/pen/aVGjay
$(function() {
var item = $(".item");
$.each(item, function(index, value) {
console.log($(value).attr("data-name"));
var everyItem = $(value).attr("data-name");
});
$("#my-input").autocomplete({
source: everyItem, //?
minLength: 1,
search: function(oEvent, oUi) {
// get current input value
var sValue = $(oEvent.target).val();
// init new search array
var aSearch = [];
// for each element in the main array
$(everyItem).each(function(iIndex, sElement) {
// if element starts with input value
if (sElement.substr(0, sValue.length) === sValue) {
// add element
aSearch.push(sElement);
}
});
// change search array
$(this).autocomplete("option", "source", aSearch);
}
});
});
.items {
width: 200px;
}
.item {
background-color: red;
margin-top: 2px;
}
<input type="text" placeholder="Filter items" id="my-input">
<div class="items">
<div class="item" data-name="one">one</div>
<div class="item" data-name="two">two</div>
<div class="item" data-name="three">three</div>
<div class="item" data-name="four">four</div>
</div>
It's a little odd to use autocomplete for this, as that's intended to build a filtered option list from a provided object or remote data source, not from DOM content.
You can build the functionality yourself by attaching an input event listener to the #my-input which in turn goes through the .item elements and uses a regular expression to filter ones with matching data-name attributes and displays them, something like this:
$(function() {
var $items = $(".item");
$('#my-input').on('input', function() {
var val = this.value;
$items.hide().filter(function() {
return new RegExp('^' + val, 'gi').test($(this).data('name'));
}).show();
});
});
.items {
width: 200px;
}
.item {
background-color: red;
margin-top: 2px;
}
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<input type="text" placeholder="Filter items" id="my-input">
<div class="items">
<div class="item" data-name="one">one</div>
<div class="item" data-name="two">two</div>
<div class="item" data-name="three">three</div>
<div class="item" data-name="four">four</div>
</div>

Find number of columns produced by ng-repeat

I have a div on which i used ng-repeat. It displays the list of photos in a grid. This is all very simple stuff and working fine.
I need to add file explorer like navigation on the items inside the grid. The easiest way would be to know the length of a line (number of items in the line) and then do simple math to calculate where to move the selection based on the key pressed.
The problem i'm having is finding out the length of the line. Is it even doable?
Edit :
<div class="images-cq__item" ng-repeat="photo in displayedPhotos">
<div ng-class="{active: photo.selected}" id="{{photo.uuid}}" ng-click="selectionEvent({value: photo.uuid, event: $event, index: $index})">
<div class="images-cq-statut" ng-show="photo.statusCQ != 'none'">
{{photo.statusCQ}}
</div>
<div class="img-cq">
<img ng-src="{{photo.thumbPath100}}" alt="Alternate Text" />
zoom
</div>
<p>
{{photo.title}}
</p>
<ul class="images-cq-tags">
<li id="{{photo.uuid}}.{{tag.value}}" ng-repeat="tag in tags" style="display:none">
<span>{{tag.name}}</span>
</li>
</ul>
</div>
The displayedPhotos is a simple array with photo objects obtained from the server. It contains several links (thumbnails, original), and some other info but i don't think it is relevant in this case.
This is what i got:
JavaScript
var items = $(".images-cq__item");
var previousTop = null;
var itemsPerRow = 0;
for(var i = 0; i < items.length;i++){
var item = items.eq(i);
var offset = item.offset();
var top = offset.top;
if(!previousTop || (top == previousTop)){
previousTop = top;
itemsPerRow++;
} else{
break;
}
}
console.log(itemsPerRow); // 3
HTML
<div class="container">
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
<div class="images-cq__item"></div>
</div>
CSS
.container{
width: 400px;
}
.images-cq__item{
width: 120px;
height: 120px;
background: green;
margin-right: 10px;
margin-bottom: 10px;
display: inline-block;
}
You can use {{$index}} inside ng-repeat
See this Angular.js. How to count ng-repeat iterations which satisfy the custom filter

Creating a filter bar with Javascript [closed]

Closed. This question needs debugging details. It is not currently accepting answers.
Edit the question to include desired behavior, a specific problem or error, and the shortest code necessary to reproduce the problem. This will help others answer the question.
Closed 7 years ago.
Improve this question
I am new to Javascript and only have very basic knowledge of it at this stage.
I am trying to create a filter bar that, when clicked, would set the opacity of the non-matched items to 0.2 and the matched item would remain at full opacity.
I have uploaded the html/css to show an example on jsfiddle: https://jsfiddle.net/rebeccasmith1301/zw2aozff/
<div id="filter-bar">
<button onclick="findShoes()">Shoes</button>
<button onclick="findTops()">Tops</button>
<button onclick="findSkirts()">Skirts</button>
</div>
<div class="product-item">
<p>Shoes</p>
</div>
<div class="product-item">
<p>Tops</p>
</div>
Skirts
I have been experimenting with javascript written on a previous post that I found very helpful but due to my basic knowledge I have been unable to solve how to achieve the results I am aiming for.
I basically would like the user to be able to click on the button shoes (for example) and all of the divs that contain the word shoes to remain with full opacity and all other divs to have the class un-selected which lowers the opacity to 0.2. The divs that contain the products can be a class only, not an id as well.
Would anyone be able to help? This would be using mainly vanilla javascript.
Many thanks,
Becky
Fiddle with multiple words: https://jsfiddle.net/qucwvqfr/1/
Fiddle with white space removal: https://jsfiddle.net/d15v3x0w/1/
Don't make a function for each possible variation of content, just make one function and give that a parameter. This javascript would check the textContent of the items, strip the whitespace from them, and change classes accordingly. The hasClass, addClass, and removeClass are helpers, focus on the highlightItems function.
function hasClass(ele,cls) {
return !!ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}
function addClass(ele,cls) {
if (!hasClass(ele,cls)) ele.className += " "+cls;
}
function removeClass(ele,cls) {
if (hasClass(ele,cls)) {
var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)');
ele.className=ele.className.replace(reg,' ');
}
}
var highlightItems = function(itemName) {
var p = document.getElementsByClassName("product-item");
for (var i = 0; i < p.length; i++) {
itemText = p[i].textContent.replace(/^\s+|\s+$/g,''); // you don't need the .replace() part if you don't add extra white space in the HTML
if ( !(itemText == itemName) ) {
addClass(p[i], "un-selected");
} else {
removeClass(p[i], "un-selected");
}
}
}
And you would use it like this:
<div id="filter-bar">
<button onclick="highlightItems('Shoes')">Shoes</button>
<button onclick="highlightItems('Tops')">Tops</button>
<button onclick="highlightItems('Skirts')">Skirts</button>
</div>
Note:
If you want to have multiple words inside the box, don't add any unnecessary white space inside the div tags. (You probably shouldn't do it anyway.) So the HTML usage would be like this:
<div class="product-item">Shoes and socks</div>
<div class="product-item">Tops</div>
<div class="product-item">Skirts</div>
Credits for the class-changing functions go to http://jaketrent.com/post/addremove-classes-raw-javascript/
There needs to be a reliable way to select the specified items. I propose that you add a class shoes, tops and skirts to their respective elements:
<div class="product-item shoes">
Shoes
</div>
<div class="product-item tops">
Tops
</div>
<div class="product-item skirts">
Skirts
</div>
Now, to select all elements that got shoes it's really easy:
var shoes = document.getElementsByClassName('shoes');
Selecting elements that don't have a class shoes is another story. Let say we start by collecting out all product-item elements, like so:
var products = document.getElementsByClassName('product-item');
From here on, you need to iterate all the elements inside the returned nodeList and check if they got a shoes class. A helper function that can help you with that:
function not(nodeList, cls){
var reg = new RegExp('\\b' + cls + '\\b');
return Array.prototype.reduce.call(nodeList, function(acc, el){
console.log(el, el.className.search(reg))
if(el.className.match(reg) === null){
acc.push(el);
}
return acc;
}, []);
}
So now, to get products that aren't shoes:
var notShoes = not(products, 'shoes');
To change the opacity of all the elements inside a nodeList we could use another helper function:
function changeOpacity(nodeList, opacity){
Array.prototype.forEach.call(nodeList, function(el){
el.style.opacity = opacity;
});
}
And to use it:
changeOpacity(shoes, 1.0);
changeOpacity(notShoes, 0.2);
All together in this snippet:
function find(cls) {
var clsList = document.getElementsByClassName(cls);
var products = document.getElementsByClassName('product-item');
var notCls = not(products, cls);
changeOpacity(clsList, 1.0);
changeOpacity(notCls, 0.2);
}
function not(nodeList, cls){
var reg = new RegExp('\\b' + cls + '\\b');
return Array.prototype.reduce.call(nodeList, function(acc, el){
console.log(el, el.className.search(reg))
if(el.className.match(reg) === null){
acc.push(el);
}
return acc;
}, []);
}
function changeOpacity(nodeList, opacity){
Array.prototype.forEach.call(nodeList, function(el){
el.style.opacity = opacity;
});
}
/* Styling for filter bar*/
#filter-bar{
width: 100%
}
#filter-bar button{
width: 30%
float: left;
margin: 0.5%;
}
/* Styling for products*/
.product-item{
width: 24%;
float: left;
margin: 0.5%;
background-color: red;
height: 80px;
box-sizing: border-box;
padding: 10px;
}
/* Different options for products with button click*/
.un-selected{
opacity: 0.2;
}
<div id="filter-bar">
<button onclick="find('shoes')">Shoes</button>
<button onclick="find('tops')">Tops</button>
<button onclick="find('skirts')">Skirts</button>
</div>
<div class="product-item shoes">
Shoes
</div>
<div class="product-item tops">
Tops
</div>
<div class="product-item skirts">
Skirts
</div>
<div class="product-item skirts">
Skirts
</div>
<div class="product-item shoes">
Shoes
</div>
<div class="product-item tops">
Tops
</div>
<div class="product-item skirts">
Skirts
</div>
<div class="product-item skirts">
Skirts
</div>
I have a solution with jquery:
HTML
<button class="active btn" id="all">Show All</button>
<button class="btn" id="a">Tops</button>
<button class="btn" id="b">Skirts</button>
<button class="btn" id="c">Shoes</button>
<!-- An element with an id is needed for the jQuery -->
<div id="parent">
<!-- The base class is the box. Categories are then given as accessory classes. Any div can be in more than one category -->
<div class="box product-item a b">Shoes & Tops</div>
<div class="box product-item a">Tops</div>
<div class="box product-item b">Skirts</div>
<div class="box product-item c">Shoes</div>
</div>
CSS
/* Styling for filter bar*/
#filter-bar{
width: 100%
}
#filter-bar button{
width: 30%
float: left;
margin: 0.5%;
}
/* Styling for products*/
.product-item{
width: 24%;
float: left;
margin: 0.5%;
background-color: red;
height: 80px;
box-sizing: border-box;
padding: 10px;
}
/* Different options for products with button click*/
.un-selected{
opacity: 0.2;
}
jQuery
var $btns = $('.btn').click(function() {
if (this.id == 'all') {
$('#parent > div').fadeIn(450);
} else {
var $el = $('.' + this.id).fadeIn(450);
$('#parent > div').not($el).hide();
}
$btns.removeClass('active');
$(this).addClass('active');
})
jsfiddle
function filter(me) {
var items = document.getElementsByClassName("product-item");
console.log(me.textContent);
for (var i = 0; i < items.length; i++) {
var item = items[i];
item.style.display = "";
if (item.textContent.trim() !== me.textContent.trim() && me.textContent.trim() !== "All") {
item.style.display = "none";
}
}
}
/* Styling for filter bar*/
#filter-bar{
width: 100%
}
#filter-bar button{
width: 30%
float: left;
margin: 0.5%;
}
/* Styling for products*/
.product-item{
width: 24%;
float: left;
margin: 0.5%;
background-color: red;
height: 80px;
box-sizing: border-box;
padding: 10px;
}
/* Different options for products with button click*/
.un-selected{
opacity: 0.2;
}
<div id="filter-bar">
<button onclick="filter(this)">Shoes</button>
<button onclick="filter(this)">Tops</button>
<button onclick="filter(this)">Skirts</button>
<button onclick="filter(this)">All</button>
</div>
<div class="product-item">
Shoes
</div>
<div class="product-item">
Tops
</div>
<div class="product-item">
Skirts
</div>
<div class="product-item">
Skirts
</div>
<div class="product-item">
Shoes
</div>
<div class="product-item">
Tops
</div>
<div class="product-item">
Skirts
</div>
<div class="product-item">
Skirts
</div>

Categories