It seems like I'm having a pretty simple problem but can't seem to find a solution.
I’m trying to convert an application from vanilla JS to the angular framework. Previously, my program had an array of objects, and clicking a button would trigger a function which would just toggle one object’s boolean value between true or false. The objects with value “true” would then be filtered out and displayed further down the page.
The function was...
function addRepair(here, needed) {
possibleRepairs[here].isNeeded = needed;
var filteredArray = possibleRepairs.filter(ind => ind.isNeeded).map(ind => ind.area + " " + ind.repair).join("\<br\>");
document.getElementById('repairsGoHere').innerHTML = filteredArray;
}
I’m having trouble recreating this in angular. I think I’ve figured out most pieces of the process, but I don’t know how to code the function that’s supposed to do the actual toggle. Basically, I’m not sure how to access the array that I’ve created and change the boolean assignment.
So far in Angular I’ve created a class “repairs” and in my file app.component.ts I’ve exported a list of “repairs” in repairList like so…
repairList = [
new repairs("Front wheel", "out of true",false),
new repairs("Front hub", "needs tightening", false),
...
];
In the app.component.html I’ve created a button which is supposed to do the toggle…
<button type="button" onclick="addRepair('0', true);">Out of true</button>
which calls a function (which I know has been imported properly)...
function addRepair(here, needed) {
repairList[here].isNeeded = needed;
}
and later on the page will display data as so…
<div class="col-md-8" id="repairsGoHere" ng-repeat="inst in repairList | filter: {isNeeded:true}">
<p>{{ inst.area }}</p>
</div>
any advice on how to code the function to get it to do what I’d like? Or should I be approaching this a totally different way?
Thanks for all the help!
Evan
After getting some responses on here y'all helped me figure it out - I was coding in Angular 6 and didn't realize ng-repeat was from the AngularJS-era. Ang 6 doesn't have the same filter features so I'll need to figure out how to achieve the same result as I was seeking. Thanks for all the input!
Related
So I'll get straight to the point, im building an angular application and I am using Angular Material Tables. Bit of a disclaimer I'm pretty new to Angular and development
So basically I'm creating a dataSource variable to where I'm going to constantly update my tables data with each call and this is the code where that happens
const siteFilter: SiteFilter = {}; this.siteControllerService.getAllSitesFilteredAndSorted(siteFilter, this.page).subscribe((data) => { this.data = data; // eslint-disable-next-line #typescript-eslint/no-non-null-assertion this.data.sort((a, b) => (a.siteId! > b.siteId! ? 1 : -1)); this.dataSource = new MatTableDataSource(this.data); this.dataSource.sort = this.sort; });
(Originally the last 3 lines were also in ngoninit and I put them into a 1 second timeout which did solve my issue but it is extremely not good at all, I'm also aware that its bad practice to put this into my constructor but its there because I am in the belief that if it runs before my oninit and I get the data and then trigger a detechchanges on my oninit it should detect that my data is not up to date with the current dom and re render it.)
my #Component looks like this:
#Component({ changeDetection: ChangeDetectionStrategy.OnPush, selector: 'app-table', templateUrl: './table.component.html', styleUrls: ['./table.component.scss'], })
And inside the ngOnInit I am calling the this.ref.detectChanges(); which is in the constructor as this:
private ref: ChangeDetectorRef
Now the reason this is all here is that right now the above HTTP call returns me an array of objects I put that object into the dataSource and my table should re render with the newly added data as far as I'm aware, but it came to my attention that sometimes I make a call to get 20 entries of the data I want, and only 14 or 16 or however much is visible on my table and so I got confused logged the response and even though my table had 16 entries the object I got was precisely 20 as much as I asked for.
Not sure how this stackoverflow works but I'll continue on inside the what did I try section so I tried to use a changeDetector since I realsed that the data is there inside my variable but its not updatin the DOM I implemented the above example and I'm still on the same issue, as if the http response time is a big longer than usual then the data I see in my table is not what the dataSource is at that moment.
Which means basically that the dom is not representing the actual data inside my dataSource.
So my question is basically is there a way for me to detect whenever my http call a 100% ended and then I do the this.dataSource = new MatTable... etc. I've tried going into the success arrow function inside the call but that didin't do much either and I've also looked up how async await works but I got really nowhere there since im really newbie at this still and have no idea how to implement an async await functionality.
Not sure if this information is relevant at all but I'll share this since If it is then thank god we got this out of the way but none of the http calls are created by me or anyone else at that matter, this generated through the dto-s the BE has and whatnot which is is somehow compiled automatically into a service and downloaded as an NPM package from an artifactory and we use the Service which this package has and we cannot edit the http calls at all we just use the respective method and give the asked data to them. Not sure if this is relevant or a good to know for the question but here it is.
If I could get any direction to go to and help that would be great.
(I asked chat GPT also if someone suggest me that, offered me the above example which didn't work.)
Okay so after 2 weeks of struggling to understand why this would hapen I have finally been able to figure out a solution if anyone is ever interested or has a similar issue this is what solved it for me.
I've moved the logic to get the data from the API to its own method as shown here:
GetAllSites() {
const siteFilter: SiteFilter = {};
this.siteControllerService
.getAllSitesFilteredAndSorted(siteFilter, this.page)
.pipe(first())
.subscribe((data) => {
this.data = data;
this.data.sort((a, b) => (a.siteId! > b.siteId! ? 1 : -1));
this.dataSource = new MatTableDataSource(this.data);
this.dataSource.sort = this.sort;
this.ref.detectChanges();
});
And now this is the first thing what is called inside my ngOnInit hook.
The only difference is that im using a .pipe(first()) before my subscription, I have played around with this solution with different sizes of data and simulation of backend delay and this seems to be working, and whenever the call is finished the dataSource is updated correctly and the dom is refreshing with the newly updated data.
What a ride.
Background: Our application is built on Angular. And I was trying to get access to underlying property of any of the component using Javascript.
For Eg:
HTML
<span class='course-list'>{{ totalCourses }}</span>
TypeScript
totalCourses = 5 //This is backend value
Using JavaScript is it possible to get access to the component instance totalCourses value..
So far I could reach till here
JS solution:
componentInstance = window.document.querySelector('course-list-detials')
// course-list-detials = this is selector for the component
by console logging this I was able to get html code for this 'course-list-detials'
We are trying to get access to component instances here. Since totalCourses value consists of backend value and we are trying to assert if the value matches with the rendered value in page using JS.
PLEASE help me!! I really need help. Any clue would also be really helpful
Thank you
I want to serialize my Sounds model for use in my game.js file. This is what I have.
views.py
def index(request):
context = {'sounds': serializers.serialize('json', Sounds.objects.all()) }
return render(request, 'index.html', context)
index.html
<button type="submit" onclick="main()">Let's Start!</button>
game.js
function main(){
var data = {{ context|safe }};
// print all objects here
}
It's not working - not sure what the issue is. Basically, when I click the button in index.html, it should go to the main function (this part works), and then set a variable data with the objects in the model so that I can use it. Also, is it possible to filter objects in data so that I have a list with only the objects with id = 1? I want to do this in game.js (not in views.py or elsewhere).
It's hard to tell what the exact problem with the serialiser is without an actual error log. If you can actually open the console that django is running in, it should display the error when it occurs. From that a better diagnostic of the issue can be made. It would be wise to do this in the future otherwise questions like this will be downvoted.
Yes, you can filter arrays in Javascript using the filter function. The function creates a new array by interating through the objects in the array that will be passed into a function, which should return true if it should exist in the new array or false it is shouldn't.
So for the example you provided, it would appear as so:
sounds = sounds.filter((sound) => { sound.id === 1; });
Note you will have to assign the new array, the original array will not change bu running the function itself.
I highly recommend you search for solutions to these problems yourself before creating threads on Slack Overflow or your reputation will quickly decrease.
I know this question has been asked in here before it seem that all the answers is either quotes from AngularJS doc or doesn't provide with a solution (not a solution I understand anyway) so I'll give it a try once more.
My experience with Angular is relatively new, started out some month ago, so please forgive my ignorance if this is basic knowledge.
Within a list of posts (iterate by using ng-repeat) I've a special "share to" button.
The link on the button (href) depends on three different factors: post_id, user_id, network.
I'm trying to do this, within my ng-repeat="post in posts"
<a href ng-href="{{genShareUrl(post.id,post.author_id,'fb')}}>Facebook</a>
The original function which perform the generation is in a factory, I just use genShareUrl as a middleman function between controller and factory.
When logging out from the genShareUrl function in the post controller, I see this function is called multiple times.
Actually, if I run it on full scale on all posts fetched from backend, my app just come to a halt. No error, just eternal loading (I figured that I might have inadvertently triggered some kind of eternally $digest loop I'm unfamiliar with or at least some exponentially call pattern).
I've tried to recreate the scenario with a simple example:
http://codepen.io/Mestika/pen/xVexRa
Here I can see, that the function first is called twice, then four times - which indicates to me that the digest cycle is triggered multiple time.
In such a case as described, how would I best go about generating some value in a link? Is this the best practice? If no, how or could you give me an example on how to refactor the code.
Angular uses dirty checking to achieve two-way binding, all two-way binding watchers would be evaluated in each digest cycle, that is the reason genShareUrl be called multiple times, to avoid this happened, you could use one-way binding in your template:
<a href ng-href="{{::genShareUrl(post.id,post.author_id,'fb')}}>Facebook</a>
I'd like suggest a best practice for your case:
Resolve start-up logic for a controller in an activate function.
So, you can add an activate function as follows:
activate();
function activate() {
angular.forEach($scope.json, function (p) {
p.btoa = $scope.func(p.id);
});
}
After that, you can update your template and use:
<div ng-repeat="person in json">
<a href ng-href="/user/{{person.btoa}}">{{person.firstName + ' ' + person.lastName}}</a>
</div>
In that way you'll avoid multiple calls to your function due to two-way data binding behavior.
Take a look following links:
Angular Styleguide
Your example updated: http://codepen.io/anon/pen/wGZBEX
I'm dynamically instanciating template on event / or array change (with observe-like functionality).
To achieve that, I use
//whatever event you want, eg:
$(".foo").on("click", function(){
Blaze.renderWithData(Template.widgetCard, d, $(".cards").get(0));
}
That is working, but obviously, instances aren't bound to any parent's template.
Because I just rendered this template on the div.cards I'm unable to use the Template.parentData(1) to get the parent datacontext, even so this div.cards is include on a template.
The quick fix would be to set the wanted reference (which in my case is an object) variable parent's datacontext on global scope, or even use Session, or directly pass this context through the renderWithData's data.
Do you know any other way,even better the proper one (I mean Meteor fancy one), to achieve that?
Is it a good Blaze.renderWithData use case?
Tell me if i'm unclear or more code is needed.
EDIT:
Complementary context info:
I've a chart (d3) where it's possible to select some parts of it.
It has an array property to stock this selected data part.
Chart = function Chart(clickCb, hoverCb, leaveCb, addSelectionCb, removeSelectionCb){
var chart = this;
chart.selectedParts = [];
//... code
}
From outside of this Chart class (so on the Meteor client side), the chart.selectedParts is modified (add/delete).
The dream would be to "bind" this array chart.selectedParts like:
Template.templateContainingAllThoseCards.helpers({
selectedDataChart: function(){
return Template.instance.chart.selectedParts;
},
//...
});
and on the template being able to do something like that:
<div class="row">
<div class="large-12 columns">
<div class="cards">
{{#each selectedDataChart}}
{{> cardWidget}}
{{/each}}
</div>
</div>
</div>
Like that, if the chart.selectedParts was reactive, Blaze could automatically create or remove cardWidget template instance due to the binding.
I've tried to use manuel:reactivearray package on it (and it's kind of anoying cause I'm doing complex manipulation on this array with Underscore, which obviously don't work with none-native Array type such reactiveArray).
Not working, but I dunno if it should have worked.
What do you think?
At this time, I'm doing things a bit dirty I suppose; I juste instanciate/destroying Blaze View on element added/removed chart.selectedParts as: Blaze.renderWithData(Template.widgetCard, {data: d, chart: this}, $(".cards").get(0));
So here how I manage to do that.
Actually I don't think using Blaze.renderWithData() is a good solution.
Best way I've found is to pass your data on "Reactive mode", to be able to use all Template functionalities, and keep using Spacebars to instanciate templates. (Like parent DataContext link).
Easiest way to have reactive datasource is to always match your data with your Mongo, so I don't have to declare a custom Reactive Data source (which could be tricky with complex from a complex js data structure).
If someone have the same problem, I'm pretty sure it's because you don't follow the "good" way to do (which was my case).
One con with always updating your DB as reactive Data source should be a case where you're doing a lot of UI state change, and after all, saving the change. On this case, it's pretty useless to always pass by the DB, but it's from far the quickest solution.
Ask me if you have any similar issue understanding philosophy/way to do, I'm starting to understand what i'm doing!