How can I use index while iterating over JSONArray in handlebar? - json

I am trying to show the details of the adultJsonArr in different and I want to get it saved on action without going into writing lengthy code.
I'm not getting idea of how to use the index while iterating, I tried as mentioned in line no 5 but didn't work( #index also couldn't do job for me).
Any suggestion on how to save the changes made to the input in the adultJsonArr
1.<form>
2. {{#each adultJsonArr as |adultTravellerDetails index|}}
3. <div id="adult_form{{index}}">
4. Adult{{index}}:
5. <input type="text" value="{{adultJsonArr.[index].traveller_name}}" required>
6. <input type="dob" value="{{adultTravellerDetails.traveller_dob}}">
7. <input type="text" value="{{adultTravellerDetails.traveller_gender}}" required>
8. </div>
9. {{/each}}
10. <div {{action "confirmTravellerUpdate" adultJsonArr }}>
11. {{mdl-button text='submit'}}
12. </div>
13.</form>

You can replace {{adultJsonArr.[index].traveller_name}} with {{adultTravellerDetails.traveller_name}}.

If you would like to access a certain element from a array you should not do this in your template. The ember devs have made it hard to do so because it would likely put to much logic into the template. Use instead a computed property or a helper to retrieve your element from the collection.
E.g. you can create a helper function that receives a collection and a index and returns the desired item.
import { helper } from '#ember/component/helper';
export function colItem(params) {
return params[0][params[1]];
}
export default helper(colItem);
And in your template:
{{helper-name collection 5}}
But remember that for this you would need an array to work. If you would like to access an item from another collection you would have to handle this accordingly.

The index should probably only be used for display purposes. There's no need to look up things within the current index of adultJsonArr because you already have access to adultTravellerDetails.
Having said that, if you really have some use-case where you need to look up the value of the array at the current index, (or maybe you have some other sorted array with meta-data that you want to look up by index?) you can do something like this:
{{get adultJsonArr (concat index ".traveller_name")}}
We are building a path with concat for get to look up on adultJsonArr. This feels a bit hacky, but it works.

Related

How to get a I18n variable value I can return to my Angular parent component?

I'm new to Angular and I just put in place an i18n (2 languages) system for a website I am creating. Everything works properly but in order to switch from one language to another in my header, I feel stuck.
I followed the Angular documentation to transfer my variables from child to parent component and I ended with this:
<input type="text" id="item-input" #lang>
<button type="button" (click)="changeChosenLang(lang.value)">
{{ 'global.lang' | translate }}
</button>
As you can see, I write my language in the input form and I send it to the proper component with a button. What I wanted was to click on my 'global.lang' text and to be able to send its value to the parent component, since the value is the language which is not actually used.
I don't know how to put my 'global.lang' text in a variable, neither what kind of balise I can use. Also I didn't know how to summarize my problem to search for it on StackOverflow so if you know a similar post, don't hesitate to post the link.
Thank you for your reading!
I found a less tortured way (poor brain) to have the result I wanted:
<span (click)="changeChosenLang()">
{{ 'global.lang' | translate }}
</span>
First I temporary changed my button to a span balise and I deleted the parameter from my changeChosenLang() function. Then, I transferred a variable 'lang' from my parent component to this one, witch contains the value of the language chosen in my app constructor. At each click, I change its value in my changeChosenLang() function and everything works great!
I hope it can help someone someday. The moral of this post is: the simpler, the better! Have a good day.

Arbitrarily run method during ngFor loop (Angular 5)

I have an angular page, where, during an *ngFor loop, I want to update a variable, then write it to the HTML during each iteration of the loop.
Like so:
HTML:
<table *ngFor="let data of Dataset">
somehowRunThis(data)
<div>{{methodResult}}</div>
</table>
TS:
...
methodResult: any;
...
somehowRunThis(data): {
let a;
...
this.methodResult = a;
}
etc etc.
Is there any way this can work? Attempting to add a method that returns within the curly brackets seems to not work, and there appears to be no effective way to run arbitrary methods from the HTML in Angular.
Thank you for any assistance you can provide.
Is there any particular reason why you want to trigger this update in HTML?
Depending on your needs you can use pipe (https://angular.io/guide/pipes) or transform the data to desired format in your component.
I would say it's not a good idea to have a method with side-call effects invoked in HTML.
There are a lot of ways to do this. A general advice: sometimes we are looking for an answer in the wrong places, be open :)
Instead of forcing ngFor, just run a simple array.map on your data before sending it to the template.
displayData = this.data.map(el => this.somehowRunThis(el))
this way you'll avoid having terrible performance.
If you don't care and still want to do this thing for some reason you can make your function return it and actually call in template:
{{ myFunctionReturnsText() }}
This is a bad idea because the function calls will run on each change detection so something like Pipes/Directives will be better.

In an Angular page containing custom components, how should I make sure all IDs are unique to get rid of the warnings?

I have a custom component for text input and each of them has an internal field ID'ed as data. It causes the warning below to appear.
[DOM] Found 13 elements with non-unique id #data
I'm clear on why it happens and I understand that's a warning not an actual error. I also recognize the appropriateness of an ID being unique (in its scope).
I'm not entirely sure regarding the implications in my particular case. In my opinion, warnings are tolerable but not acceptable.
Is there a best-practice approach to get rid of the error? By the very concept of a GP component, some parts will be alike in each instance. Or is there a trick to unique'fy the IDs? Or perhaps a directive or such to let Angular know we're cool with the state as is?
The component uses #ViewChild("data") to refer the input control in the template below.
<div id="outer">
...
<label for="data">{{label}}</label>
<input #data id="data" ... >
<div *ngFor="let error of errors" class="row"> ... </div>
</div>
As far as I understand the purpose of using ids is querying it inside of Angular. You could use a directive or another attribute to query without any warnings. Also you could make a kind of wrapper which would apply common ID to input and its label and just concat UUID and ID you want to use. But if it's only about querying just choose another attribute. For example data-id or data-qa whatever gives you an ability to query and have no errors at the same time. Just in case #ViewChild("data") refers to #data and not id="data" whilst you may wrap input with label tag.

Angular JS - Character counter across multiple text inputs

I have an Angular JS app I am building along with Angular-UI (bootstrap).
I have a set of multiple input boxes, which the user can input into and then that input is binded into a div. What I would like to do is have a character count that applies to all the boxes, so its one limit on all boxes and as the user types into them boxes the overall counter is affected. So far I can do this:
<p>xxxxxxxx?</p>
<textarea class="form-control" rows="3" ng-model="what[$index]" id="input" maxlength="200"></textarea>
<span>{{200 - what[$index].length}} left</span>
So this will give me a limit on that box, but how Can i get it so I have a overall counter? I have about 8 more text boxes and they are binded into a div as follows:
<div ng-repeat="w in what">
<p style="font-size:22px;"></p>
<p>{{w}}</p>
</div>
app.js
$scope.what=[];
Any help would be appreciated?
Two Way Data-Binding
First you need to set up 2 way data-binding so that when a user makes a change to the model it has an effect on the controller. you will use $scope for this since scope represents your model in Angular. then to build a running total I have 2 options for you.
Quick and Dirty
A quick and dirty way to count up a total across different controllers and other parts of your angular app would be to use $rootScope.YourCountVar. Root scope is a global variable and has its own host of problems because of this.
declare a counter near the top of your application (after angular.module) in app.js like this
.run(function ($rootScope) {
$rootScope.counter= 0;
})
Now in your controllers that you would like to count you need to set the value of $rootScope like
$rootScope.counter += $scope.YourInputData.length;
Repeat that for each of your inputs.
Best Practice
Create a Service that you can instantiate everywhere you need to add onto the running total. This way you only have the service where you need it. Doing things this way is much safer and cleaner and I would recommend it.

Transferring attributes in Angular directive

I guess I have to make the jump to directives.
I have a directive in my html:
<ev-filter placeholder='{{pp.srcListFilter}}'
ng-model='data.srcFilter'/>
that I want to replace (using a template) with:
<div class='filterbox'>
<input id='srcFilter' type='text'
placeholder='{{pp.srcListFilter}}'
data-ng-model='data.srcFilter'>
</div>
What I can't seem to get my head around is how to "transfer" the attributes id, placeholder, ng-model, etc,... from the directive to the template. The variable value swill be different for each use, so I might have:
<ev-filter placeholder='{{pp.srcListFilter}}'
ng-model='data.srcFilter'/>
or
<ev-filter placeholder='{{pp.subjectListFilter}}'
ng-model='data.subFilter'/>
i.e. the variable names are not hardcoded. All the example online seem to manage the variables using scope, but they assume the names are hardcoded (always the same). I can't do that because I might have multiple instances on a single page/managed by a single controller