Angular.copy keeps giving me the same object - javascript

I have an AngularJS application that manages badges. In the application is a form to set the badge # and the name of the person it is assigned to, etc. This gets stored in $scope.badge.
When the user submits the form, I want to add the new badge to a list of badges, which is displayed below the form.
Partial code looks like this:
var badge = angular.copy($scope.badge); // make a copy so we don't keep adding the same object
$scope.badgeList.push(badge);
The first time I run this code, it adds the badge as expected.
Any subsequent time I run this code, the next badge REPLACES the previous badge in the badgeList. In other words, if I add 5 badges, the badgeList still only has 1 object in it because it just keeps getting replaced.
I'm thinking that this may be happening because the same object keeps getting added? Maybe I'm wrong? I am using angular.copy to try and avoid that happening, but it doesn't seem to be working.
Any thoughts on this?

$scope.badgeList.push(($scope.badge);
console.log($scope.badgeList)
no need to use angular.copy since you are ultimately storing all the badges in an array
angular.copy is used when you want to make a clone of object and not update the existing object and the clone's change are not reflected in main object.
If you just want to maintain a list of badges you can execute this block of code
like this
function addBadges(){
$scope.badgeList.push(($scope.badge);
console.log($scope.badgeList)
}
If you are refreshing the controller then obviously the variable will be reset and for such a case you need to make use of angular services.
Create a service and inside the service you need to define getter and setter method that will help in data persistence
and your bages array if saved in service will persist till the application is in foreground.

You could do something like this.
function addBadges(){
//initialize if undefined or null
if(!$scope.badgeList){
$scope.badgeList = [];
}
//Check if badge does not exists in the list
if ($scope.badgeList.indexOf($scope.badge) === -1) {
//Add to badge list
$scope.badgeList.push($scope.badge);
}
}

Related

QML variable value get zeroed while changing the page

I am using stackView and with a button press I chance the page by using push and pop. I want to save same variable value and when I open the page again, I want to keep continue using that variable with its value when page is closed.That is why I declared the variables in the main qml :
Window{
id:main
property var isSomethingOn:false
StackView{
id:contentFrame
initialItem:Qt.resolvedUrl("qrc:/MainPage.qml")
Connections{
target:contentFrame.currentItem
onBackButtonPressed:{
contentFrame.pop()
}}}}}
and in the other page I use that variable like:
Item{
id:Page1
signal backButtonPressed()
Image{
id:button1
MultiPointTouchArea{
main.isSomethingOn = !main.isSomethingOn
if(main.isSomethingOn){
button2.buttonImageColor="Imgs/button_left_red.png"
}
else{
button2.buttonImageColor="Imgs/button_left_blue.png"
}
}
}
Now I expect isSomethignOn to be saved and not to be deleted between page transitions. But it indeed get deleted. How can I solve that issue?
The value is getting saved, but you probably can't tell because bindings don't automatically work with var properties. var properties do not automatically notify when their value changes. You can manually trigger them by calling isSomethingOnChanged(), but there's no reason to do that unless you actually need your property to be a var. In your case changing it to a bool should be all you need to do.
For your additional question about push and pop, see the docs. Particularly this line:
This means that any item pushed onto a StackView will never be
destroyed by the StackView; only items that StackView creates from
Components or URLs are destroyed by the StackView.

Material Table not reflecting changes on datasource

This is my first question in Stack Overflow. I'll try to be specific but I don't know how to keep this short, so this is going to be a long post. Sorry about that. I promise I searched and tried a lot of stuff before asking, but I'm kind of lost now.
I'm developing a simple app in Angular 6 to keep track of software requisites and the tests associated to those requisites.
I have a component, called RequisiteList, whose HTML part consists in a mat-table with an Array of my own Requisite model class as [dataSource]. This array is received as an #Input parameter, and it also has an #Output parameter which is an EventEmitter that notifies and passes to the parent component every time a Requisite on the list is clicked.
I make use of RequisiteList inside of ReqListMain, which is a component consisting on the list and a hierarchical tree for filtering. This component is working fine, showing, and filtering requisites as intended. This component also captures the #Output event of the list and passes it as an #Output to its parent.
Finally (for what it's related to this question), I have a TestView component that has both an instance of RequisiteList to show the requisites currently associated to current test, and an instance of ReqListMain to add new requisites to current test (like a "browser"). This TestView has an instance of the model class Pectest corresponding to the test that is being currently visualized, which has an array of Requisite.
The idea in this last component was that whenever a requisite of the "browser" list was clicked, it was added to the current test's list. In order to do that, in the callback method associated to the #Output event of the browser list, I tried to add the Requisite received as a parameter:
addrequisite(requisite: Requisite) {
this.currentTest.requisites.push(requisite);
console.log('Current test: ');
console.log(this.currentTest);
}
In the HTML part of TestView, I inserted the RequisiteList component like this:
<app-requisitelist [requisites]="currentTest.requisites" ngModel name="reqlistview"></app-requisitelist>
(The ngModel property is part of the things I've been trying, I'm not sure it's necessary).
The result is:
The clicked requisite is not shown in the list.
In the console output I can see the content of currentTest object, and I verify that clicked requisites are in fact added to the requisites array of that object, so the event fires and the object is passed upwards by the children components.
I'm not sure if my problem is that data binding is made by value (I don't think so, as I bind an Array, which is an object AFAIK), or the table is not detecting data changes (I've tried to force data change detection with ChangeDetector), or anything else.
You pass a array to the app-requisitelist component. This component waits this array changes to update the content. When you do this.currentTest.requisites.push(requisite), the array this.currentTest.requisites doesn't change, I mean, if you do
const tmp = this.currentTest.requisites;
this.currentTest.requisites.push(requisite)
if (tmp === this.currentTest.requisites) {
console.log('The arrays are the same');
}
You will get the log printed. So, I suggest do something like that:
addrequisite(requisite: Requisite) {
this.currentTest.requisites.push(requisite);
this.currentTest.requisites = this.currentTest.requisites.map(item => item);
console.log('Current test: ');
console.log(this.currentTest);
}
The inserted line forces this.currentTest.requisites to be a new array with the same content.

Angular: *ngFor not updating view after array changes

I'm making a web app with Angular however I'm having trouble with the load button. So the user presses the load button, selects a save to load, and a new set of properties is loaded into forms that each constitute a step-item in a stepper. The forms array that loads all of the forms is 'reset' with this function:
private resetPropForms(): void {
this.propForms = [];
}
This is the function that receives as an argument a properties event and then sets the forms array:
onPropertiesEmitted(properties: Property[]): void {
this.resetPropForms();
this.resetDividedProperties();
this.resetUndividedProperties();
this.setDividedProperties( properties );
this.setForms();
this.setUndividedProperties( properties );
}
And this is what my template looks like:
<se-stepper
*ngIf="undividedProperties"
[linearMode]="false"
[(activeStepIndex)]="activeStepIndex"
>
<se-step-item
*ngFor="let form of propForms"
>
<app-properties-form
[propertiesForm]="form"
(change)="onPropertiesChanged($event)"
>
</app-properties-form>
</se-step-item>
</se-stepper>
Lastly, the view is updated but only after I go to the next step and come back. And if I save the properties then the correct value is sent to the backend even though the value displayed in the view is incorrect/not updated. Any ideas why this is happening. I tried using trackBy with a unique identifier for each form ( using a random number ) but that didn't work. I tried using ChangesRef and detectChanges() and that didn't work. When I leave out the *ngFor and just display the first form in the array it updates properly so that leads me to believe that this problem has something to do with the *ngFor.
edit:I'm pretty sure setTimeout() placed in onPropertiesEmitted() worked but I forgot where I put it to make it work and it seemed like a not-so-great solution to the problem
I Think the problem is that the array is the same and change detect won't detect changes, try this
private resetPropForms(): void {
//this.propForms = [];
//array.slice() returns new array
this.propForms = this.propForms.slice(0,0);
}
You could also reset the form before assigning new properties ( form.reset() )

Observable fires only once

I decided to pick up this RxJS tutorial over the weekend to learn about reactive programming. The goal is to set up a simple page that generates an interactive list of users from the Github users API using Observables.
The list displayed is a subset of the total number of users retrieved (in my case 3 out of 30). The list needs to be refreshable (show a new set of users), and you need to be able to remove entries from it by clicking the 'remove' button on each respective entry.
I've set up a chain of Observables to define the behavior of the page. Some act as events to trigger processing, and some publish processed results for use in the app. This chain should dynamically cause my list to be updated. Currently, the default flow is like this:
Startup!
Suggestions refresh triggered! (this is where the data is retrieved)
30 new suggestions available!
List update triggered! (this is where I choose 3 users to display)
List updated! (at this point, the list is showing on the page)
A list update is triggered on startup, by refreshing the list and by removing something from the list. However, when I refresh the list, this happens:
Refresh button clicked!
Suggestions refresh triggered!
30 new suggestions available!
As you can see, the trigger to update the list of users is not set off. As I understand it, by emitting some value at the start of a stream, the rest of the stream should be executed consequently. However, this only seems to be happening the first time I run through the chain. What am I missing?
You can find a running version of my project here.
I think the issue is in the way userStream$ Observable is created.
Filtering users not closed and then taking the first 3 is something that can be done directly on the UserModel[] array passed into the pipe chain by displayEvents$ via filter and slice methods of Array.
If you do so, you remove the need of using the from function to create an Observable<UserModel> on which you then have to apply flatMap (which is currently better known as mergeMap) to apply finally toArray to transform it back into an Array of UserModel.
In other words you can simplify the code as in the following example, which as side effect solves the refresh problem.
this.userStream$ = this.displayEvent$.pipe(
map(users => users
.filter((user: UserModel) => !this.closedUsers.has(user))
.slice(0, this.numberOfUsers))
// flatMap((users: UserModel[]) => from(users))
// // Don't include users we've previously closed.
// , filter((user: UserModel) => !this.closedUsers.has(user))
// , take(this.numberOfUsers)
// , toArray()
, tap(() => console.log('List updated!'))
// Unless we explicitly want to recalculate the list of users, re-use the current result.
, shareReplay(1));
To be honest though I have not fully grasped why your original solution, which is a sort of long detour, does not work.

Controller belonging to render helper gets reset if passed a model

I noticed a behavior in Ember that does not make any sense to me. I am not sure if this is a bug or feature. In the latter case I am really interested why this is a desired behavior. So here we go:
Make sure you can see your browsers console output.
Open the example project on JS Bin. Notice the two init messages comming from IndexController and FoobarController.
Click on the button saying 'Add one'. Do this so that there is some state on the FoobarController.
Click on the 'Go to hello' link to transition to the hello route
Go back to index via the link
The count variable still has the value. All good!
Now there is a tiny change in the next JS Bin. I pass the model to the render helper.
Follow the steps above again. After step 5 you see that count is 0 now and that the 'init FoobarController' appears again.
Somehow a controller belonging to a render helper gets reset when a model is passed. I can't find any information on why this happens or think of any reason why this makes sense.
From the Docs
If a model property path is specified, then a new instance of the controller will be created and {{render}} can be used multiple times with the same name.
Passing that second param re-instantiates the FoobarController, which basically resets the count to 0, whereas not passing the model param creates a singleton instance of the FoobarController.

Categories

Resources