Angular Select2 formatResult not updating the template - angularjs

I have a select2 drop down with the following markup:
<select id="selectByName" ui-select2="select2Options" ng-model="selectId" data-placeholder="Select item by name" style="width:250px">
<option></option>
<option ng-repeat='item in items' data-show="{{item.show}}" value="{{item.id}}">
{{item.name}}
</option>
</select>
And the js contains the following:
$scope.items (an array that has a id, a boolean show property and a name property)
and the select 2 options:
select2Options : {
allowClear: true,
placeholder:"select a value",
formatResult: function(state) {
var $elem = angular.element(state.element),
isVisible = $elem.data('show');
return isVisible ? '<span style="color:red">'+state.text+'</span>':
<span style="color:blue">'+state.text+'</span>';
}
},
Well, the ng-repeat updates correctly the html markup and sets data-show attribute to either true or false, but the formatResult function does not update this value.
In the html source the data-show="true" and in the formatResult function $elem.data('show') = false;, why doesn't it update while the function is called every time the select2 is opened?
Here is made a plunker that illustrates my question: plnkr.co/edit/d0LxuhzdQh7hMdzOoxpr?p=preview .It looks the formatResult updates the results correctly only once before opening the select2 for the first time.

Edit
http://plnkr.co/edit/6Vma1WTQWQw0HAIQUVxE?p=preview
$scope.select2options = {
allowClear: true,
placeholder: "select a value",
formatResult: function(state, container) {
var $elem = angular.element(state.element);
var scope = $elem.data('$scope');
if (scope !== undefined) {
isVisible = scope.$eval($elem.data('show'));
$scope.dataShow[$elem.attr('value')] = isVisible;
$scope.updated++;
return isVisible ? '<span style="color:red">' + state.text + '</span>' :
' <span style="color:blue">' + state.text + '</span>'
}
}
}
The key part is grabbing the $scope data from the jqLite element and then calling $eval, which evaluates an unparsed string expression in the context of the scope. If we had used $scope.$eval, it would have used the controller $scope, which wouldn't have the ng-repeat on it. By grabbing it from the element we have a scope that has access to the item property for the ng-repeat.
Having said that I don't recommend using this code (sometimes jQuery widgets force you into unpleasant corners when working with angular). Again if you find yourself manipulating angular.element or using $element in a controller you probably should use a directive instead. Then again we programmers have to deal with non-ideal constraints (time, money, etc.) that prevent us from working "ideally" so given your context this may be a decent solution.
Let me know if any of my explanation doesn't make sense.
Original
http://plnkr.co/edit/vYTdxPwgwqZSgK5m9yk9?p=preview
Is this what you want?
JavaScript
$scope.items = [{
id: 1,
show: false,
name: 'test1'
}, {
id: 2,
show: true,
name: 'test2'
}, {
id: 3,
show: true,
name: 'test3'
}];
$scope.selections = [1, 2];
$scope.getStyleForIndex = function (index) {
var item;
for (var i = 0; i < $scope.items.length; i++) {
if (i === index) {
item = $scope.items[i];
break;
}
}
return item.show ? { color: "red" } : { color: "blue" };
}
$scope.select2options = {
allowClear: true,
formatResult: function(item, container) {
var color = $scope.getStyleForIndex(parseInt(item.id, 10)).color;
container.html('<span style="color:' + color + '">RESULT ' + item.text + '</span>');
},
formatSelection: function(item, container) {
container.append($compile('<span ng-style="getStyleForIndex(' + item.id + ')">SELECTION ' + item.text + '</span>')($scope));
}
}
HTML
<div ng-repeat="item in items">
{{ item.name }}
<input type="checkbox" ng-model="item.show" />
</div>
<select ui-select2="select2options" ng-model="selections" style="width:200px" multiple="true" ng-options="i.id as i.name for i in items"></select>
{{selections}}

Related

AngularJS Table Column Search...smarter ideas?

I have to use AngularJS to build a dashboard and one of the components is a table.
Since I did not find relevant dependencies/libraries for angularjs (like tabulator or datatables), I am doing it myself.
Instead of using the native angular filter, I built a custom method, but I am not sure if I am following a good approach.
The main idea is that when I pull the data object (array of objects) via Ajax, I create both an "original" and a "current" data object,s and at the beginning, they are exactly the same of course.
Then I created an input field above every column heading and I linked the search function to the blur and keyup events (enter key).
When the search function is triggered, I start making changes to the "current" object. This way I can filter by multiple columns incrementally. I filter the data object using an awesome library called AlaSQL.
I also linked to a button the "reset" method, which simply makes the "current" object equal to the "original" object, and cleans up the input fields.
The point is, am I missing any best practices? Are there better ways to do so with AngularJS?
Any suggestions?
Thanks a lot.
HTML
<div ng-app="myApp">
<div ng-controller="divController">
<my-table></my-table>
</div>
</div>
JS
var app = angular.module('myApp', []);
app.controller('divController', function ($scope, $http) {
$scope.data = {};
$scope.data.current = null;
$scope.data.original = null;
$scope.filter = {
id: {
field: "id",
value: null
},
name: {
field: "name",
value: null
},
owner: {
field: "owner",
value: null
},
}
$scope.reset = function () {
console.log("reset");
$scope.data.current = $scope.data.original;
for (let prop in $scope.filter) {
$scope.filter[prop]["value"] = null;
}
}
$scope.filterExec = function (field, value) {
if (value) {
console.log(`Executing filter on field "${field.trim()}" by this value "${value.trim()}"`);
var filtered = alasql('SELECT * FROM ? where ' + field + ' LIKE "%' + value + '%"', [$scope.data.current]);
$scope.data.current = filtered;
}
}
$http.get("./workspaces_demo_obj.json")
.then(function (response) {
console.log(response);
$scope.data.original = response.data;
$scope.data.current = response.data;
});
});
app.directive('myTable', function () {
return {
template:
'<div>Total rows {{data.current.length}} <button ng-click="reset()">RESET</button></div>' +
'<table class="table table-responsive table-sm">' +
'<thead>' +
'<tr><th>Workspace ID</th>' +
'<th>Name</th>' +
'<th>Owner</th></tr>' +
'<tr><th><input ng-model="filter.id.value" ng-blur="filterExec(filter.id.field, filter.id.value)" ng-keydown="$event.keyCode === 13 && filterExec(filter.id.field, filter.id.value)" placeholder="Filter by id"></input></th>' +
'<th><input ng-model="filter.name.value" ng-blur="filterExec(filter.name.field, filter.name.value)" ng-keydown="$event.keyCode === 13 && filterExec(filter.name.field, filter.name.value)" placeholder="Filter by name"></input></th>' +
'<th><input ng-model="filter.owner.value" ng-blur="filterExec(filter.owner.field, filter.owner.value)" ng-keydown="$event.keyCode === 13 && filterExec(filter.owner.field, filter.owner.value)" placeholder="Filter by owner"></input></th></tr>' +
'</thead>' +
'<tbody>' +
'<tr ng-repeat="x in data.current">' +
'<td>{{ x.workspace_id }}</td>' +
'<td>{{ x.name }}</td>' +
'<td>{{ x.owner }}</td>' +
'</tr>' +
'</tbody>' +
' </table>',
restrict: 'E'
};
});

Ionic toggle group check one item and uncheck the others

I have created a toggle view to select available items in Ionic, and if anyone of the item were selected, I want to uncheck all the other items. I also have a scan function which allows me to dynamically update the items list
I'm fairly new to ionic, so I just have the following code in my settings.html
<ion-toggle ng-repeat="item in itemsList"
ng-model="item.checked">
{{ item.text }}
</ion-toggle>
and then I have created a simple settings.js:
(function () {
'use strict';
angular.module('i18n.setting').controller('Settings', Settings);
SettingController.$inject = ['$scope'];
function Settings($scope){
$scope.settingsList = [
{text: "item1", checked: true},
{text: "item2", checked: false}
];
}
})();
I know ng-model="item.checked" will do the job of changing the attribute $scope.settingsList.checked for me. But what I want to know this how to use it to check one items and uncheck all the other ones?
loop through all the items, set the checked state of all the values to false and then your html code must be:
<ion-toggle ng-repeat="item in settingsList"
ng-model="item.checked"
ng-checked="item.checked" style="border:1px solid #28a54c" ng-change="toggleChange(item)">
{{ item.text }}
</ion-toggle>
Your Controller code
$scope.toggleChange = function(item) {
if (item.checked == true) {
for(var index = 0; index < $scope.settingsList.length; ++index)
$scope.settingsList[index].checked = false;
item.checked = true;
} else {
item.checked = false
}
};
And it's better to use forEach in async environment.
Angular 2+ Version, Ionic 4
HTML
<div class="toogle" *ngFor="let item of toogleConfig">
<div class="toogle__title">{{item.title}}</div>
<ion-toggle [(ngModel)]="item.checked" (ngModelChange)="ToogleChange(item.id)" color="success"></ion-toggle>
</div>
</div>
TS
public toogleConfig = [
{id:0, title:'Recurrent', checked: false},
{id:1, title:'One time', checked: false},
]
public ToogleChange(index:number) {
this.toogleConfig.forEach(toogle => { toogle.checked = false; });
this.toogleConfig[index].checked = true;
}

How to access item data from dxList ,Devextreme Mobile

HTML
<div data-bind="dxList: { dataSource: dataSource }">
<div data-options="dxTemplate : { name: 'item' } " >
<div class="list-item" data-bind="text: name"></div>
<div data-bind="dxCheckBox: {value: check }"></div>
</div>
</div>
<div data-bind="text: 'Save', click: save"></div>
Javascript
var dataSource = ko.observableArray([]);
dataSource.push({ name: "name1", check: true });
dataSource.push({ name: "name2", check: false });
save: function () {
}
How to get 'name' and 'check' values inside save function,Devextreme mobile?
You just can use the dataSource array.
var save = function () {
var items = dataSource();
console.log(items[0].name + " - " + items[0].check());
console.log(items[1].name + " - " + items[1].check());
};
If you want to get only checked items, you can use the jQuery.grep function to filter data.
var items = $.grep(dataSource(), function(item){
return item.check() === true;
});
Also, I suggest you use the ko.observable() to define the check field of items. It allows you to track changes of the check field.
http://jsfiddle.net/d4t1pqby/3/

ng-table filter with nested properties

I have following JSON:
[{
"Id": "1",
"Data": {"Str1": "Ann", "Str2": "Xenna"}
},{
"Id": "2",
"Data": {"Str1": "Bob","Str2": "Bobby"},
}]
And I created ng-table to display it. I tried to add filter. When I filter by Id everything works as expected (filter is { "Id": "2" }). But I cannot create proper filter do Str1 and Str2 fields. I already tried:
{ "Str1": "A" }
{ "Data.Str1": "A" }
{ "Data['Str1']": "A" }
but above options does not work.
Example of my work is here: http://plnkr.co/edit/MyJCqTlgvKLtSP63FYQY?p=preview
Update
Thanks to #Blackhole I founded that filter {Data: {Str1: 'A'}} works. But I can only delcare this in code. When I try to put something like this in HTML it doesn't even show filter:
<td data-title="'Str1'" filter="{Data:{Str1: 'text'}}">
{{ user.Data.Str1 }}
</td>
When you try to use filter="{Data:{Str1: 'text'}}" in html,input doesn't showing cause of template in header,have a look in source code.
<div ng-repeat="(name, filter) in column.filter"> //!!!! right here it's not supported
<div ng-if="column.filterTemplateURL" ng-show="column.filterTemplateURL">
<div ng-include="column.filterTemplateURL"></div>
</div>
<div ng-if="!column.filterTemplateURL" ng-show="!column.filterTemplateURL">
<div ng-include="'ng-table/filters/' + filter + '.html'"></div>
</div>
</div>
right here <div ng-repeat="(name, filter) in column.filter"> it's not dig into nested objects
Ngtable not support nested filter in default template,so you can create your own template,which gonna support it.Have a look to example of header template.
Note
This how column.filter is initializing,it's parsing from filter attribute on td tag,source
var parsedAttribute = function (attr, defaultValue) {
return function (scope) {
return $parse(el.attr('x-data-' + attr) ||
el.attr('data-' + attr) ||
el.attr(attr))
(scope, {
$columns: columns
}) || defaultValue;
};
};
var parsedTitle = parsedAttribute('title', ' '),
headerTemplateURL = parsedAttribute('header', false),
// here
filter = parsedAttribute('filter', false)(),
filterTemplateURL = false,
filterName = false;
...
columns.push({
....
filter: filter,

AngularJS checkbox filter directive

I have a AngularJS directive that allows users to select a values from a list to filter on. Pretty simple concept which is represented here:
Problem is when I click one of the checkboxes they all select unintended. My directive is pretty simple so I'm not sure why this is happening. The code around the selection and checkboxes is as follows:
$scope.tempFilter = {
id: ObjectId(),
fieldId: $scope.available[0].id,
filterType: 'contains'
};
$scope.toggleCheck = function (id) {
var values = $scope.tempFilter.value;
if (!values || !values.length) {
values = $scope.tempFilter.value = [];
}
var idx = values.indexOf(id);
if (idx === -1) {
values.push(id);
} else {
values.splice(idx, 1);
}
};
$scope.valuesListValues = function (id) {
return $scope.available.find(function (f) {
return f.id === id;
}).values;
};
and the data resembles:
$scope.available = [{
id: 23,
name: 'Store'
values: [
{ id: 124, name: "Kansas" },
{ id: 122, name: "Florida" }, ... ]
}, ... ]
the view logic is as follows:
<ul class="list-box">
<li ng-repeat="val in valuesListValues(tempFilter.fieldId)">
<div class="checkbox">
<label ng-click="toggleCheck(val.id)">
<input ng-checked="tempFilter.value.indexOf(val.id) === -1"
type="checkbox"> {{val.name}}
</label>
</div>
</li>
</ul>
First off, it toggleCheck fires twice but populates the correct data ( second time given my code it removes it though ).
After the second fire, it checks all boxes... Any ideas?
Perhaps its that the local variable doesn't get reassigned to the property of the scope property used in the view. Since your values are then non-existent and not found, the box is checked.
$scope.tempFilter.value = values
I took the interface concept you were after and created a simpler solution. It uses a checked property, found in each item of available[0].values, as the checkbox model. At the top of the list is a button that clears the selected items.
JavaScript:
function DataMock($scope) {
$scope.available = [{
id: 23,
name: 'Store',
values: [{
id: 124,
name: "Kansas"
}, {
id: 122,
name: "Florida"
}]
}];
$scope.clearSelection = function() {
var values = $scope.available[0].values;
for (var i = 0; i < values.length; i++) {
values[i].checked = false;
}
};
}
HTML:
<body ng-controller="DataMock">
<ul class="list-box">
<li>
<button ng-click="clearSelection()">Clear Selection</button>
</li>
<li ng-repeat="val in available[0].values">
<div class="checkbox">
<label>
<input ng-model="val.checked"
type="checkbox" /> {{val.name}}
</label>
</div>
</li>
</ul>
</body>
Demo on Plunker
The repeat that I used to grab the values based on the id, was the problem area.
<li ng-repeat="val in valuesListValues(tempFilter.fieldId)">
removing that and simple listening and setting a static variable resolved the problem.
$scope.$watch('tempFilter.fieldId', function () {
var fId = $scope.tempFilter.fieldId;
if ($scope.isFieldType(fId, 'valuesList')) {
$scope.valuesListValues = $scope.valuesListValues(fId);
}
}, true);
});
and then in the view:
ng-repeat="value in valuesListValues"

Resources