Angular, comparing 2 objects and replacing (possibly concatinating?) - javascript

I have 2 multi-select lists in angular that are dependent on on another. One is the parent list and one is the child list. When landing, there is nothing in the child list and once you select (one or many) items from the parent multi-select, it will then populate the child list with the children of the selected item(s) from the parent list. This works great - just for reference I have a $watch on the parent model, so when it changes (the user selects something in the parent list - it will then call an $http and fetch the results for the children list
like so -
$scope.$watch('selectedResources', function (newValue) {
angular.forEach($scope.selectedResources, function(data){
$scope.generalIDArray.push(data.id);
});
//id's = $scope.imageIDArray
//place new data in $scope.imageOptionsSub
$http({
method: 'POST',
url: '/listSubCategories',
data: {
page: 0,
ids: $scope.generalIDArray
}
})
.success(function(data){
//empty options
$scope.resourceOptionsSub = [];
//push new data in
angular.forEach(data.subCategories, function(index) {
$scope.resourceOptionsSub.push(index);
});
});
So - I push all the id's in and send them to get the results back.
However - here is my problem. I realized after trying this out that I don't want to completely replace resourceOptionsSub with all the new results, because the user has interacted with the child results - they have a .checked value on them that means the user has selected them, this will be wiped out and refreshed each time I make a new call because it empties out the scope and replaces it, even if it is the same items.
What I would like to happen is to kind of compare if there are items that already exist in resourceOptionsSub that are coming in with the call, and sort of keep the original resourceOptionsSub and compare it to the new one coming in, and maybe pull off the items that don't exist any more?
I'm thinking I should somehow compare the 2 objects 1 being the original, 2 being the new - and if 2 has anything 1 does not have, then pull it out of 1, because then I could keep the items that are the same untouched. If anyone could point me in the right direction here as to where to look I would much appreciate it, as I am at a bit of a loss as to how to handle this. Thanks for reading!

angular.extend will work but you will need start with an empty object like so:
var updatedObj = angular.extend({}, obj1, obj2);

Related

Trying to dynamically organize JSON object elements into different arrays based on values

This is the JSON I'm working with:
https://data.cityofnewyork.us/resource/xx67-kt59.json?$where=camis%20=%2230112340%22
I'd be dynamically making the queries using different data, so it'll possibly change.
What I'm essentially trying to do is to somehow organize the elements within this array into different arrays based on inspection_date.
So for each unique inspection_date value, those respective inspections would be put into its own collection.
If I knew the dates beforehand, I could easily iterate through each element and just push into an array.
Is there a way to dynamically create the arrays?
My end goal is to be able to display each group of inspections (based on inspection date) using Angular 5 on a webpage. I already have the site up and working and all of the requests being made.
So, I'm trying to eventually get to something like this. But of course, using whatever dates in the response from the request.
2016-10-03T00:00:00
List the inspections
2016-04-30T00:00:00
List the inspections
2016-04-12T00:00:00
List the inspections
Just for reference, here's the code I'm using:
ngOnInit() {
this.route.params.subscribe(params => {
this.title = +params['camis']; // (+) converts string 'id' to a number
this.q.getInpectionsPerCamis(this.title).subscribe((res) => {
this.inspectionList = res;
console.log(res);
});
// In a real app: dispatch action to load the details here.
});
}
I wish I could give you more info, but at this point, I'm just trying to get started.
I wrote this in jQuery just because it was faster for me, but it should translate fairly well to Angular (I just don't want to fiddle with an angular app right now)
Let me know if you have any questions.
$(function() {
let byDateObj = {};
$.ajax({
url: 'https://data.cityofnewyork.us/resource/xx67-kt59.json?$where=camis%20=%2230112340%22'
}).then(function(data) {
//probably do a check to make sure the data is an array, im gonna skip that
byDateObj = data.reduce(function(cum, cur) {
if (!cum.hasOwnProperty(cur.inspection_date)) cum[cur.inspection_date] = [];
//if the cumulative array doesn't have the inspection property already, add it as an empty array
cum[cur.inspection_date].push(cur);
//push to inspection_date array.
return cum;
//return cumulatie object
}, byDateObj);
//start with an empty object by default;
console.log(byDateObj);
}, console.error);
});
<script src="https://ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>

knockout js, add new item to an array

So this follows on from my previous question:
knockout js, add additional elements to an array
Basically I have an app where a user fills in certain data, clicks next and this is added to an array. However, what I'd like to do is add some items into the array before the user even begins using the app (these items I get from a database). The idea being that at the start they can view each item in the array and then choose and an item and edit this item. I've got a feeling I'm missing something blindingly obvious but I cannot seem to figure it out
Knockout observable arrays have equivalent functions to native JavaScript arrays. See: http://knockoutjs.com/documentation/observableArrays.html
So you need just to use arr.pop(item) or arr.push(item).
In case you need to replace all items and want to avoid multiple events to raise, use observableArray.valueWillMutate() and valueHasMutated() functions. See sample where I do swap the entire array:
ko.observableArray.fn.replaceWith = function (valuesToPush) {
// NOTE: base on - ko.observableArray.fn.pushAll
var underlyingArray = this();
var oldItemcount = underlyingArray.length;
this.valueWillMutate();
// adding new items to obs. array
ko.utils.arrayPushAll(underlyingArray, valuesToPush);
// removing old items (using KO observablearray fnc.)
if (oldItemcount > 0)
this.removeAll(underlyingArray.slice(0, oldItemcount));
this.valueHasMutated();
return this; //optional
};

IN CQ, how to set value of all the items in Panel to blank

In ExtJS panel I need to set value of all items (e.g. textfield, pathfield) to blank. I don't want to set value of each individual item to blank but of whole panel in one go.
I am able to get list of items
function getAllChildren (panel) {
/*Get children of passed panel or an empty array if it doesn't have thems.*/
var children = panel.items ? panel.items.items : [];
/*For each child get their children and concatenate to result.*/
CQ.Ext.each(children, function (child) {
children = children.concat(getAllChildren(child));
});
return children;
}
but how to set to blank for whole panel? Please suggest what need to be done in this case.
Actually, it's not possible to do it with one liner - all at the same time. What your method returns is purely an array of objects. In fact if such syntax existed, it would iterate over all fields anyway.
Though clearing all fields, having the method you've proposed is very trivial to do. Just iterate over them all and call reset method. Mind some (especially custom) widgets might not handle it.
var fields = getAllChildren(panel);
CQ.Ext.each(fields, function(field) {
if (child.reset) {
child.reset();
}
});
You've got similar loop in your getAllChildren code - you might reset field at the same place.
The method is defined in Field type which is usually a supertype of each dialog widget. You can read more here.

Using $watch to update data, ng-repeat not reflecting changes

I have a list of fooditems in $scope.raw and I want to show this data in columns so I'm changing the structure a bit. I do this in the sortStuff() function and store the updated data in $scope.allfood. There's a $watch that calls sortStuff() every time anything changes in $scope.raw (I'm using drag and drop to change the food category):
$scope.$watch('raw', function(){
$scope.allfood = $scope.sortStuff();
console.log($scope.allfood);
}, true);
This is what happens when food is dragged around:
receive:function(event, ui) {
var issueScope = angular.element(ui.item).scope();
scope.$apply(function() {
var recp = _.find(scope.raw, function(lineitem){
return lineitem.name === issueScope.receipe.name;
})
recp.cat = scope.col.name;
})
$(ui.item).remove(); // remove DOM
}
Basically, I search for the right object inside $scope.raw and change cat to new category for the food. I also delete the dom element because I'm counting on ng-repeat to refresh the view. This seems to work fine: console.log inside $watch shows that the object is being moved to the right category and the data looks what it should look like. However, visually, ng-repeat doesn't reflect the data.
Here's the jsfiddle.
Dragging an item from B to C works fine. Dragging one from A to B, makes two items from B disappear... the results are very inconsistent and I have no idea what is happening.
Any ideas what is going wrong? Or maybe any suggestions for a better way to do this?
The problem with your code is that the ng-repeat directive adds the property $$hashKey to every element in the list. This property is used by the directive to associate DOM elements with array elements.
Because you are passing the elements by reference, the ng-repeat directive writes the $$hashKey property directly into the objects of your $scope.raw array. A simple workaround is to copy the objects before inserting them into the $scope.allfood object.
_.each($scope.raw, function(recp){
recp = _.clone(recp);
switch(recp.cat){
...
}
});
Now the ng-repeat updates the objects of $scope.allfood, while the objects of $scope.raw remain untouched.
See the updated fiddle:
http://jsfiddle.net/b8Fa7/5/

Kendo UI - Property Changed MVVM

I seem to be having issues databinding 'calculated' fields with Kendo UI.
I am attempting to do data-binding with several what I will call 'calculated' fields. I have a grid, several buttons, filters and some sorting on a page that are all using the same datasource, an observable array called 'allItems'. allItems is populated via a service call and sorted, manipulated and otherwise changed while the user is on the page via buttons.
There are several navigational buttons and several div's that are populated based on information in the previous item, current item and next item in accordance to the current filters and sorts applied. Those buttons contain information in them that is extracted from the previous, current and next items as they related to the allItems list (ie the objects are actually held in the allItems array and are in fact observable objects).
so in the viewmodel object I have something like this (please excuse short handing):
var viewmodel = kendo.observable({
var allItems = observablearray[]
var currentIndex = 1; //or other default value
var changeCurrentItem = function(){
var self = this;
//do some stuff
//stuff might include modification to allItems
self.set("currentIndex", someNewValue);
}
var previousItem = function(){
return self.get('allItems')[currentIndex - 1];
}
var currentItem = function(){
return self.get('allItems')[currentIndex];
}
var nextItem = function(){
return self.get('allItems')[currentIndex + 1];
});
return viewmodel;
The buttons and other info boxes are bound to previous,current and next Items. But this does not appear to work. I've had to make previous, current and nextItems copies of what lives in the allItems array and update those 3 objects at the same time. Not that big of a deal, but I just would like to, you know, not store copies of objects if I don't have to. I was hoping there might be a NotifyPropertyChanged("MyProperty") similiar to C#/Xaml that I missed while going through the API. This sort of functionality will be most useful to me for future tasks I have on my list due to some of the complexities of our calculated fields and the need to reducing memory consumption as devices get smaller.
Thanks for any help,
~David
Whenever a property of the view model changes, the change event is triggered.
To make this work in calculated fields, you need to access the relevant fields using ObservableObject.get(). See this section in the documentation.
2 possible problems.
1) How do you get items into allitems?
2) You should also .get("currentIndex") since that is the value that you update in changeCurrentItem
var currentItem = function(){
return self.get('allItems')[self.get('currentIndex')];
}
That should cause viewModel to fire its change event for thecurrentItem calculated field whenever allItems or currentIndex changes.
If all else fails, you can manually fire the change event by doing:
viewmodel.trigger("change", { field: "currentItem" });
viewmodel.trigger("change", { field: "previousItem" });
viewmodel.trigger("change", { field: "nextItem" });
which would be similar to calling NotifyPropertyChanged("currentItem") in XAML.

Categories