How can I add a custom search condition to a smart table ?
Usually, we set the search for a column like this :
<input st-search="description" placeholder="Description..." type="search"/>
But is there a way to use a custom function for the search ?
Using st-set-filter will change the behavior of st-search and this is not what I want.
For now, I'm adding the condition when building the table lines like this :
<tr ng-repeat="request in r.displayedRequests"
ng-if="r.isInArray(r.developersList, request.developers && (request.status != 'CAN')">
<td>{{request.id}}</td>
<td>{{request.description}}</td>
<!-- ... -->
</tr>
Doing this filters the proper lines but this makes the pagination fail, since the displayedRequests is not updated like when using st-search.
So, how can I add a condition using my controller variables to filter my table lines ?
(And still being able to filter by global or column search using st-search on different text inputs)
Doing this filters the proper lines but this makes the pagination fail
you should really not filter your table rows using ng-if when using ng-repeat. ng-repeat expressions can take their own filter arguments and can accept your own custom filters.
ng-repeat="item in vm.items | filter: thoseIDislike"
this will also kill your pagination, so use the st-safe-src directive to save a safe copy of your data and repeat by it
sample plunker
But is there a way to use a custom function for the search ?
yes, you can use ng-change on the input, when you will change the input's model value, the function will run (to prevent it from running on each key stroke, i'm using ng-model-option's debounce property)
<input st-search="firstName"
placeholder="first name"
ng-model="fname"
ng-model-options="{ debounce: 1000 }"
ng-change="alertMe(fname)">
in the controller:
$scope.alertMe = function(message) {
if (message.length>0) {
alert(message);
}
}
Related
I'm using Angular 1.4.7. I didn't set up this site so I'm not sure what information I need to give for this. We're rendering some search filters, but whether or not they're enabled is configured by data retrieved from the server, for example:
$scope.searchFields = {
"date": true,
"price": true,
...
};
The HTML for a search field ($scope.filter being an individual set of vars for this particular filter, in this case it's just selecting a maximum numeric value):
<div class="limit-group clearfix">
<span class="detail">Filter Name:</span>
<div class="filter-select-small">
<select name="filter" class="custom-select" ng-if="searchFilters.code">
<option value="">No Max</option>
<option ng-repeat="(key, value) in filter" value="<%key%>"><%value%></option>
</select>
<span class="unavailable" ng-if="!searchFilters.code">Unavailable</span>
</div>
</div>
There are conditions where the available search fields will change. The select fields, for whatever reason, get replaced by a jQuery plugin called selectbox. I have a function resetFilterStyles() which reattaches the jQuery plugin to the fields, but I'm not sure where to fire it.
Inside of methods defined on $scope the data is being updated using $http.get() but running resetFilterStyles() inside of these anonymous functions does not work, presumably because Angular hasn't processed yet that the data has been updated, so the changes that would be performed by resetFilterStyles() are undone by Angular's updates to the DOM.
I've tried setting a $watch handler but this seems to be the wrong place to run my function as it doesn't appear to take, either (and additionally causes grievous errors within Angular). As someone who doesn't use Angular, I'm not sure where to go from here.
Edit
The reset function:
function resetFilterStyles() {
// desktop adv filters
var filterContainer = $('.advance-filter-dropdown');
$('select',filterContainer).selectbox('detach');
$('select',filterContainer).selectbox('attach');
$(".filter-select-small .sbOptions").niceScroll({cursorborder:"",cursorcolor:"#ccc",autohidemode: false});
// mobile adv filters
var filterContainer = $('.filter-wizard-mobile');
$('select',filterContainer).selectbox('detach');
$('select',filterContainer).selectbox('attach');
$(".filter-select-small .sbOptions").niceScroll({cursorborder:"",cursorcolor:"#ccc",autohidemode: false});
}
From the AngularJS perspective, you shouldn't do DOM modifications inside the controller. You should create a directive/component to handle that for you.
But if you really must have that jQuery (legacy projects, for example), you can
put your DOM modification into $timeout:
$http.get("url").then(function() {
$timeout(function() {
// updateDom
}, 0);
});
I have a standard enough dynamically filled table in bootstrap that is instantiated with the following definition
<table>
<tr ng-repeat="c in controller.items :
orderBy:controller.predicate:controller.reverse">...</tr>
</table>
And a pretty standard function that, when called with a string argument, will sort the table, using the string argument as the predicate.
controller.orderCasesBy = function (predicate) {
controller.reverse = (controller.predicate === predicate) ?
!controller.reverse : false;
controller.predicate = predicate;
}
The page I'm working on potentially has upwards to a thousand rows, so I was trying to figure out a way to track the progress of the ordering function and update a progress bar accordingly.
If this were a case where I was trying to track the progress of the entire table rendering for the first time, I could attach an ng-init function to each repeated row that could update the progress bar whenever it's called. ng-init functions don't seem to be called when a table is reordered, however, so I was wondering if there was anything I could do to achieve a similar effect.
If I can't, I'd love to hear suggestions on alternative ways to track this.
You can add an object parameter to the ordering function, which updates its value property (or call it whatever you want), then use the object.value to render your status indicator as you see fit.
Since you said you're open to alternatives, you don't need to actively reverse it at all (and therefore don't need to indicate progress for it). ngRepeat allows you to track by index, which means you can simply display them in reverse index order.
Example:
<table>
<tr ng-repeat="c in controller.items track by $index">
<td ng-model="controller.items[controller.items.length-($index+1)]"></td>
</tr>
</table>
I'm trying to do a search with product results using AngularJS. I've obtained an JSON object with my results": example below
[{"store_id":"17","user_id":"29","company_name":"Liquor R Us","company_type":"Alcohol",
"phone":"(303) 555-5555","website":"http:\/\/liqourrus.com","address":"5501 Peoria St",
"address_2":"","city":"Denver","state":"CO","zip":"80239","lat":"39.796181",
"lng":"-104.84863","mon_open":"","mon_close":"","tues_open":"","tues_close":"",
"wed_open":"","wed_close":"","thurs_open":"","thurs_close":"","fri_open":"","fri_close":"",
"sat_open":"","sat_close":"","sun_open":"","sun_close":"","distance":"1.1668156112981596",
"s":"s","st":"Every Store"}]
I'm using ng-repeat="store in storeResult" and the results will not display until I click on my Filter function, please note the filter is not applied!
<div class="product" ng-repeat="store in storeResult">
{{store.company_name}}
</div>
ng-click="setFilter('all');"
$scope.setFilter = function(filter) {
if(filter == 'all') {
$scope.searchProduct.product_type = '';
$scope.searchStore.company_type = '';
}
}
If I click the "setFilter" button, all results show. I'm trying to figure out how to make it display without having to click the button.
This work around only works in Chrome. Firefox and IE, never display results.
Plunker: link
My best guest is that by clicking the setFilter button you are triggering a digest cycle and your ngRepeat will be executed because of that. I suspect that you are assigning your storeResult outside the digest cycle and that's the reason is not displaying initially. I cannot tell for sure because is not in your description how is that JSON assigned to your storeAssignment .
Can you check how is that variable assigned?
Can you change the way it's and do this:
$timeout(function() {
$scope.storeAssignment = _your_json_value;
});
You will have to include the $timeout dependency but with this, a change in storeAssignment will be inside your digest cycle and the ngRepeat will see that change.
Hope that helps.
It looks like you're not setting the filter to 'all' until you click the filter function. Make sure that filter starts out as 'all'.
I have a simple Angular app which has some data, and an input box to filter that data.
<input class="form-control" ng-model="filters.generic" />
<h3>{{filteredUsers.length}} filtered users</h3>
<ul>
<li ng-repeat="user in filteredUsers = (data.userData | filter:filters.generic )">
{{user.registration.firstName}}
</li>
</ul>
The 'filteredUsers' variable stores the users with the applied filter. That all works absolutely fine. However, I'd like to be able to add a watch to execute a particular function whenever this 'filteredUsers' variable changes (whenever the user types anything in the input box).
I do not want to simply add a method to ng-change on the input, because I want to introduce other filters elsewhere.
I thought that I could do this in my controller with
$scope.$watch('filteredUsers', function () {
alert('something');
});
This executes once, when the application loads, and then never again. What must I do to make this watch for changes to filteredUsers?
You might need to use $watchCollection() instead of $watch()
Keep in mind that the filter on an ng-repeat could be running way more frequently than you need or expect it to, and adversely affect the performance of your code.
Even though you said you don't want to use ng-change, I think you should reconsider that and only update your array when needed.
<input class="form-control" ng-model="filters.generic" ng-change="genericFilterChange()" />
var updateFilteredUsers = function(){
$scope.filteredUsers = $filter('filter')($scope.data.userData, $scope.filters.generic);
alert('something');
};
var genericFilterChange = function(){
updateFilteredUsers();
updateOtherFilteredThings();
};
So I have a modal box that allows the user to edit / save some data.
I just want to add that unlike other Meteor apps, I don't want to save the data straight away - I want the user to fill in all the fields before hitting save where it will save to the database and send to server etc. This is mainly because I want the user to be able to hit the "cancel" button to revert all changes.
I have a drop down box at the start of the form where depending on the value, fields will be shown or hidden
<select class="form-control" id="ddlNewInputType" placeholder="Enter your input type">
<option value="input">Input</option>
<option value="formula">Formula</option>
</select>
And I have a handlebar around a field like this to determine whether I want to show it
{{#if isFormula }}
<div class="row">
<input type="text"
id="txtNewInputFormula" placeholder="Enter formula">
</div>
{{/if}}
With a helper looking like this
isFormula: ->
$('#ddlNewInputType').val() == 'formula'
However, this doesn't work. Aside from when it first loads onto the page, it never hits isFormula, probably because Meteor doesn't consider any of the HTML elements as reactive so it never re-evaluates when the HTML element changes.
What is a suitable way to get around this? Is it possible to make something reactive explicitly in Meteor? I was also considering putting the dropdown list value into a session variable but that just seems messy because I'm going to need to manage this session variable (remember to clear it when the modal box closes etc.)
Your analysis is correct - a reactive variable needs to be involved in order for your helper to reevaluate after changing the select element. The basic strategy looks like:
Initialize a reactive variable when the template is created.
Whenever the select changes, update the reactive variable.
Read the reactive variable in your helper.
Rather than use a session variable, let's use a ReactiveVar scoped to your template. Here's an example set of modifications:
Template.myTemplate.helpers({
isFormula: function() {
return Template.instance().isFormula.get();
}
});
Template.myTemplate.events({
'change #ddlNewInputType': function (e, template) {
var isFormula = $(e.currentTarget).val() === 'formula';
template.isFormula.set(isFormula);
}
});
Template.myTemplate.created = function() {
// in your code, default this to the current value from
// your database rather than false
this.isFormula = new ReactiveVar(false);
};
Remember that you'll need to also do:
$ meteor add reactive-var
See my post on scoped reactivity for a full explanation of this technique.