This has been driving me crazy as I'm sure it's simple, but I've tried a multitude of different approaches over the last 2 days and nothing worked properly.
I have two select-dropdowns in my view HTML:
<p>Spend between
<select
id ='minSpend'
ng-model ="cheapest"
ng-options="offer.PriceAUD as '$'+Math.round(offer.PriceAUD) for offer in offers | uniquePrice:'PriceAUD'">
</select>
and
<select
id ='maxSpend'
ng-model ="dearest"
ng-options="offer.PriceAUD as '$'+Math.round(offer.PriceAUD) for offer in offers | uniquePrice:'PriceAUD'" >
</select>
They populate nicely with unique values once data is received.
The uniquePrice filter looks like this:
app.filter('uniquePrice', function() {
return function(input, key) {
if (typeof input == 'undefined'){return;}
var unique = {};
var uniqueList = [];
for(var i = 0; i < input.length; i++){
if(typeof unique[input[i][key]] == "undefined"){
unique[input[i][key]] = "";
input[i][key] = Math.round(input[i][key]);
uniqueList.push(input[i]);
}
}
return uniqueList;
};
});
What I like is the lowest price and highest price to be selected initially. I managed to write a fn that does it "on-click":
$scope.setValues = function(){
$scope.cheapest = $scope.offers[0].PriceAUD;
$scope.dearest = $scope.offers[ $scope.offers.length-1].PriceAUD;
}
[ $scope.offers comes from the DB sorted by PriceAUD ASC ]
Triggered in the HTML by a simple:
<button ng-click="setValues()">Set Values</button>
Which works fine. But how can I trigger this when data-loading is complete? I tried:
$scope.offers = Items.query();
$scope.offers.$promise.then(function (result) {
$scope.setValues();
});
But it doesn't work.
And when I set $scope.dearest = $scope.offers[0].PriceAUD in the controller directly it doesn't work because the promise isn't resolved. I'm sure I just need to put this in the right place, but where?
Thanks for your help.
EDIT: The code to get Items:
var appServices = angular.module('appServices', ['ngResource']);
appServices.factory('Items', ['$resource',
function($resource){
var url = '/api/get/offers.json';
var paramDefaults = {};
var actions = "{ query: {method:'GET', params:{id:'Items'}, isArray:true}";
var optns = '';
return $resource(url, paramDefaults, actions , optns);
}]);
Related
I wanted to show all the things inside my array, but it only shows the latest loops. Below is my controller function:
/*global angular*/
var app = angular.module('statisticsApp', []).controller('myCtrl',
function ($scope, $http) {
"use strict";
return $http({
method : "POST",
url : "GatewayAPI.php",
}).then(function mySuccess(response) {
$scope.records = response.data;
var mydata,myJSON,myresult,myjava, myobj;
var i;
var Result;
var chartResultTemp = [];
var resultType = [];
for(i=0; i<72;i++)
{
//storing data
mydata = $scope.records.data[i];
//retrieving data
Result = mydata.data.substring(6,9); //throw this in
myobj = mydata.data.substring(3,4);
resultType = mydata.data.substring(3, 4);
if(resultType === "A") { //selects type = a
chartResultTemp = mydata.data.substring(6,9);
} ;
$scope.test2=chartResultTemp; //this one
$scope.test3 = resultType;
console.log(Result);
console.log(resultType);
}
$scope.gotTemp = false;
$scope.gotHumidity = false;
$scope.getSoilMoisture = false;
});
});
This is the code in my php where i list all the items in my array:
<ul>
<li data-ng-repeat="cd in test2">{{cd}}</li>
</ul>
And this is my current result as viewed in browser console:
As you can see from the console log, it only shows the latest loop. I wanted to show all the data inside the array. Is it because I have declared the array wrongly.
UPDATE:
so I have used $scope.test2.push(chartResultTemp), it gave me an error: Cannot read property 'push' of undefined.
Then, i tried
if(resultType === "A") {
chartResultTemp.push([mydata.data.substring(6,9)]);
} ;
It works, but the problem is the items listed has brackets in it.
You need to push to the array
$scope.test2.push(chartResultTemp);
By using (=) you are assigning different values to the same scope in each iteration which ultimately holds the final value assigned to it. To store all the values, you have to create an array and push value from each iteration.
$scope.test2 = [];
for(let i=0; i<72; i++){
.....
.....
$scope.test2.push(chartResultTemp);
.....
.....
I have an empty javascript array(matrix) that I created to achieve refresh of divs. I created a function to dynamically put data in it. Then I created a function to update the Array (which I have issues).
The Data populated in the Array are data attributes that I put in a JSON file.
To better undertand, here are my data attributes which i put in json file:
var currentAge = $(this).data("age");
var currentDate = $(this).data("date");
var currentFullName = $(this).data("fullname");
var currentIDPerson = $(this).data("idPerson");
var currentGender = $(this).data("gender");
Creation of the array:
var arrayData = [];
Here is the function a created to initiate and addind element to the Array :
function initMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
var isFound = false;
// search if the unique index match the ID of the HTML one
for (var i = 0; i < arrayData.length; i++) {
if(arrayData[i].idPerson== p_currentIDPerson) {
isFound = true;
}
}
// If it doesn't exist we add elements
if(isFound == false) {
var tempArray = [
{
currentIDPerson: p_currentIDPerson,
currentGender: p_currentGender,
currentFullName: p_currentFullName,
currentDate: p_currentDate, currentAge: p_currentAge
}
];
arrayData.push(tempArray);
}
}
The update function here is what I tried, but it doesn't work, maybe I'm not coding it the right way. If you can help please.
function updateMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
for (var i = 0; i < arguments.length; i++) {
for (var key in arguments[i]) {
arrayData[i] = arguments[i][key];
}
}
}
To understand the '$this' and elm: elm is the clickableDivs where I put click event:
(function( $ ) {
// Plugin to manage clickable divs
$.fn.infoClickable = function() {
this.each(function() {
var elm = $( this );
//Call init function
initMatrixRefresh(elm.attr("idPerson"), elm.data("gender"), elm.data("fullname"), elm.data("date"), elm.data("age"));
//call function update
updateMatrix("idTest", "Alarme", "none", "10-02-17 08:20", 10);
// DĂ©finition de l'evenement click
elm.on("click", function(){});
});
}
$('.clickableDiv').infoClickable();
}( jQuery ));
Thank you in advance
Well... I would recommend you to use an object in which each key is a person id for keeping this list, instead of an array. This way you can write cleaner code that achieves the same results but with improved performance. For example:
var myDataCollection = {};
function initMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
if (!myDataCollection[p_currentIDPerson]) {
myDataCollection[p_currentIDPerson] = {
currentIDPerson: p_currentIDPerson,
currentGender: p_currentGender,
currentFullName: p_currentFullName,
currentDate: p_currentDate,
currentAge: p_currentAge
};
}
}
function updateMatrix(p_currentIDPerson, p_currentGender, p_currentFullName, p_currentDate, p_currentAge) {
if (myDataCollection[p_currentIDPerson]) {
myDataCollection[p_currentIDPerson] = {
currentGender: p_currentGender,
currentFullName: p_currentFullName,
currentDate: p_currentDate,
currentAge: p_currentAge
};
}
}
Depending on your business logic, you can remove the if statements and keep only one function that adds the object when there is no object with the specified id and updates the object when there is one.
I think the shape of the resulting matrix is different than you think. Specifically, the matrix after init looks like [ [ {id, ...} ] ]. Your update function isn't looping enough. It seems like you are trying to create a data structure for storing and updating a list of users. I would recommend a flat list or an object indexed by userID since thats your lookup.
var userStorage = {}
// add/update users
userStorage[id] = {id:u_id};
// list of users
var users = Object.keys(users);
I know this was asked multiple times already, but none of that answered my question.
I have the following:
I get data over JSON to Javascript into a two dimensional array.
When I load the site, the table shows up like wanted.
Now when I click a button (just for testing), it is updating one value from the array and logging that array in the console, where I see the changed array.
The problem is that the change is not showing up in the table.
When I just add a value to the array, it is showing up in the table.
What am I doing wrong?
HTML:
<table id="sortsTable">
<tbody data-bind="foreach: sorts">
<tr>
<td data-bind="text: $data.name"></td>
<td data-bind="text: $data.ingName"></td>
</tr>
</tbody>
</table>
<button data-bind="click: addPerson">Add</button>
JS:
var sorts = ko.observableArray([]);
$(function() {
var request = new XMLHttpRequest();
var formData = new FormData();
var responseElements = [];
request.open("POST", "scripts.php", true);
formData.append("action", "getSorts");
request.onreadystatechange = function() {
if (request.readyState == 4 && request.status == 200) {
responseElements = JSON.parse(request.responseText);
sorts = convertList(responseElements);
ko.applyBindings(new AppViewModel(sorts));
}
}
request.send(formData);
});
function convertList(response) { //just the function to convert the json object to a more useful array
var names = [];
var ingredients = [];
var sorts = [];
for (var index = 0; index < response.length; index++) {
var name = response[index]['name'];
var ing = response[index]['ingName'];
if (names.indexOf(name) == -1) {
names.push(name);
}
if (ingredients.indexOf(ing) == -1) {
var nameIndex = names.indexOf(name);
if (ingredients[nameIndex] == undefined) {
ingredients[nameIndex] = ing;
} else {
ingredients[nameIndex] = ingredients[nameIndex] + ", " + ing;
}
}
}
for (var i = 0; i < names.length; i++) {
sorts[i] = {};
sorts[i]['name'] = names[i];
sorts[i]['ingName'] = ingredients[i];
}
return sorts;
}
function AppViewModel(data) {
var self = this;
self.sorts = data;
self.addPerson = function() {
console.log("click");
self.sorts[0]["name"] = "test"; //doesn't update table
//self.sorts.push({name: "qwer", ingName: "we"}); //works like expected
console.log(self.sorts);
};
}
Thanks.
The observable array only monitors which items are added to it, not the items themselves, so
self.sorts.push({name: "qwer", ingName: "we"}); //works like expected
works because you're getting the observable array to add to it's items, but
self.sorts[0]["name"] = "test"; //doesn't update table
doesn't work because the observable array has no way of knowing that an item inside it has changed. For this to work the properties of the items in the array will need to be observable.
In convertList switch to:
for (var i = 0; i < names.length; i++) {
sorts[i] = {};
sorts[i]['name'] = ko.observable(names[i]);
sorts[i]['ingName'] = ko.observable(ingredients[i]);
}
And they must be set by calling the observable setter method like so:
self.addPerson = function() {
console.log("click");
self.sorts[0]["name"]("test");
...
Also as an aside, you seem to have some other issues here. You define sorts as an observable array on the first line, but you overwrite it with the return value of convertList which is a normal array, not an observable one.
sorts = convertList(responseElements);
ko.applyBindings(new AppViewModel(sorts));
I'd remove the first line and create sorts as an observable array
function convertList(response) { //just the function to convert the json object to a more useful array
var names = [];
var ingredients = [];
var sorts = ko.observableArray([]);
...
The issue is that when you bind this:
<td data-bind="text: $data.name"></td>
$data.name is not observable, it's a simple property on an object, created here:
sorts[i]['name'] = names[i];
Knockout will quite happily bind properties like this, and display them, but any updates to them are not visible to knockout. Instead, as well as your observableArray, you also need to make any individual properties you want the ability to update observable as well:
sorts[i]['name'] = ko.observable(names[i]);
Then when you update it, knockout will see the change. Note however that you can't simply just assign to the property, as you'll just overwrite the knockout observable and it will be lost, instead you need to call the observable with the update:
self.sorts[0]["name"]("test");
I am running a few tables that use the pipe/ajax section of the code with the controller/service setup. https://lorenzofox3.github.io/smart-table-website/#section-pipe
One of the issues I am coming across is when there are special characters in the values with a space in it and it is unable to filter it. For example, if there was a lastname of "last-name firstname" it is unable to filter the data but it is able to filter the name "last-name" and it is able to do "lastname firstname" just fine.
Could I get some help on figuring out why this might not be able to filter correctly?
Thank You!
Edit: I noticed I forgot to add the filter.
app.filter('propsFilter', function() {
return function(items, props) {
var out = [];
if (angular.isArray(items)) {
var keys = Object.keys(props);
items.forEach(function(item) {
var itemMatches = false;
for (var i = 0; i < keys.length; i++) {
var prop = keys[i];
var text = props[prop].toLowerCase();
if (item[prop].toString().toLowerCase().indexOf(text) !== -1) {
itemMatches = true;
break;
}
}
if (itemMatches) {
out.push(item);
}
});
} else {
// Let the output be the input untouched
out = items;
}
return out;
};
});
app.controller('mainCtrl', ['$scope', '$window', 'Resource', function($scope, $window, service) {
var ctrl = this;
this.displayed = [];
$scope.itemsByPage = $window.datatableperPage;
this.callServer = function callServer(tableState) {
ctrl.isLoading = true;
var pagination = tableState.pagination;
var start = pagination.start || 0;
var number = pagination.number || 10;
service.getPage(start, number, tableState).then(function(result) {
ctrl.displayed = result.data;
tableState.pagination.numberOfPages = result.numberOfPages; //set the number of pages so the pagination can update
ctrl.isLoading = false;
});
};
}]);
app.factory('Resource', ['$q', '$filter', '$window', '$http', '$timeout', function($q, $filter, $window, $http, $timeout) {
var nameData = [];
$http.get($window.datatableSource).success(function(response) {
nameData = response;
});
function getPage(start, number, params) {
var deferred = $q.defer();
var filtered = params.search.predicateObject ? $filter('filter')(nameData, params.search.predicateObject) : nameData;
if (params.sort.predicate) {
filtered = $filter('orderBy')(filtered, params.sort.predicate, params.sort.reverse);
}
var result = filtered.slice(start, start + number);
$timeout(function() {
//note, the server passes the information about the data set size
deferred.resolve({
data: result,
numberOfPages: Math.ceil(filtered.length / number),
});
}, $window.datatableTimeout);
return deferred.promise;
}
return {
getPage: getPage
};
}]);
Update:
With Hardy's Help I was finally able to replicate the issue.
When setting the
$scope.itemsByPage = -1;
the filtered results seem to vanish after initial couple characters at the slice
result = filtered.slice(start, start + number);
In this example, I added the word "Clinical - " to the beginning of a name and it is unable to search for the word when you start typing the word "Clinical" but other words work just fine.
https://plnkr.co/edit/7n68AKbwQGpVdFOpbUuP?p=preview
Okay, I've traced all the functions and the problem in Plunker is in this very line. Try to change the value to any positive integer and you will see that your app works just fine.
$scope.itemsByPage = -1;
The reason you get strange filtering results in some cases is because this very value is used as number later in
var result = filtered.slice(start, start + number);
thus translating to
var result = filtered.slice(0, -1);
.slice(0, -1) returns a shallow copy of the filtered array excluding the last item. And when you only have one filtered result you will recieve an empty array.
VoilĂ !
Update
As it turns out, the whole reason of using $scope.itemsByPage = -1 was a delusion of the behaviour of the library in that case.
The desired behavior was to "always return all the rows" and it can be easily achieved by using Infinity
$scope.itemsByPage = Infinity
This way we will get
var result = filtered.slice(0, Infinity);
thus getting initial filtered array in the output.
I have a handful of simple checkbox lists that I need to create for an application. I built a "Check All" button for my initial test and it worked beautifully. But, when I changed the code to fetch a subset of the list via a Node call, the list still appeared as expected, but the Check All functionality no longer did. In my initial test, the list was just an array of objects with "description" and "value" but after inserting Node into the middle, the objects also had a $$hashkey property. I'm not sure if this is the source of the problem, but if someone could take a look and tell me what's wrong, I'd appreciate it.
My HTML looks like this:
<div class="row">
<div class="col-md-3" id="semRushApiList_None">
<input type="checkbox" value="semRushCheckAll_None" name="semRushCheckAll_None" ng-click="toggleSemRushApiTypes_None()" /><strong>Check All</strong>
<div ng-repeat="apiCall in semRushApiTypes_None">
<input type="checkbox" name="selectedSemRushApiTypes_None[]" value="{{apiCall.apiName}}" ng-checked="selectedSemRushApiTypes_None.indexOf(apiCall) > -1" ng-click="toggleSemRushApiSelection_None(apiCall)" /> {{apiCall.description}}
</div>
</div>
</div>
My angular js looks like this:
$scope.semRushCheckAll_None = false;
$scope.semRushApiTypes_None = [];
fetchApiTypesByCategory("none").then(function(types){
$scope.semRushApiTypes_None = types;
});
$scope.selectedSemRushApiTypes_None = [];
$scope.toggleSemRushApiTypes_None = function() {
$scope.semRushCheckAll_None = !$scope.semRushCheckAll_None;
if ($scope.semRushCheckAll_None) {
$scope.selectedSemRushApiTypes_None = angular.copy($scope.semRushApiTypes_None);
} else {
$scope.selectedSemRushApiTypes_None = [];
}
};
$scope.toggleSemRushApiSelection_None = function(apiCall) {
var idx = $scope.selectedSemRushApiTypes_None.indexOf(apiCall);
if (idx > -1) {
$scope.selectedSemRushApiTypes_None.splice(idx, 1);
} else {
$scope.selectedSemRushApiTypes_None.push(apiCall);
console.log(JSON.stringify($scope.selectedSemRushApiTypes_None));
}
};
function fetchApiTypesByCategory(category) {
var deferred = $q.defer();
$http.get(rootApiUrl + "/fetchSemRushApiTypesByCategory?category=" + category).then(function(response) {
deferred.resolve(response.data);
}, function(response){
deferred.reject("Error: " + response.data);
});
return deferred.promise;
}
The node call looks like this:
server.route({
method:"GET",
path:"/fetchSemRushApiTypesByCategory",
handler:function(request,reply){
var q = Qs.parse(request.query);
return reply(factory.createApiTypeList(q["category"])).code(200);
}
});
and the factory looks like this:
exports.createApiTypeList = function(category) {
var result = [];
for (var i = 0; i < semRushApiJson.length; i++) {
if (semRushApiJson[i].category === category) {
var description = semRushApiJson[i].description;
var apiName = "";
for (var p = 0; p < semRushApiJson[i].params.length; p++) {
if (semRushApiJson[i].params[p].key == "type") {
apiName = semRushApiJson[i].params[p].value;
break;
}
}
result.push({
"description": description,
"apiName": apiName
});
}
}
return result;
};
Some simple console.log statements have proven that things are being populated as expected, with the exception of the $$hashkey property on the objects coming out of the Node call.
When I check the checkboxes individually, the selected array is populated with a value that doesn't have the $$hashkey and when I check the check all, the selected list gets all of the appropriate values including the $$hashkey, but the checkboxes do not get updated on the UI like they did before I moved the populating of the list to a Node call.
Can anyone tell me what I'm doing wrong here?
V