nCore nCore - 2 months ago 75
AngularJS Question

angular2 bind ngModel from ngFor

So I'm rendering my

textarea
dynamically using
ngFor
however I'm not sure how I can pass the
ngModel
to bind it in my function.

<div *ngFor="let inputSearch of searchBoxCount; let i = index" [ngClass]="{'col-sm-3': swaggerParamLength=='3', 'col-sm-9': swaggerParamLength=='1'}">
<textarea name="{{inputSearch.name}}" id="{{inputSearch.name}}" rows="3" class="search-area-txt" attr.placeholder="Search Product {{inputSearch.name}}"
[(ngModel)]="inputSearch.name"></textarea>
</div>


textarea example:
enter image description here

textarea is render based on the length of the response I get from api call in my case
searchBoxCount
is basically
searchBoxCount.length
, so if it
length
is = 1 then it will only render 1
textarea
if its 3 then it will show 3
textareas
. The objs have different names (example: id/email/whatever), so ngModel is based on the obj name from the json object.

How do I bind
inputSearch.name
to my function
getQueryString()


getQueryString() {
this.isLoading = true;
let idInputValue = inputSearch.name; //bind it here

return "?id=" + idInputValue
.split("\n") // Search values are separated by newline and put it in array collection.
.filter(function(str) {
return str !== ""
})
.join("&id=");
}


Search func where getQueryString() is called

searchProduct() {
let queryString1 = this.getQueryString();
this._searchService.getProduct(queryString1)
.subscribe(data => {
console.log(data);
});
}


I know how to do it if the
ngModel
is not coming from the
ngFor
, is there another way to get the value from the
textarea
without
ngModel
? maybe that's the only way or if I can still use
ngModel
please show how to do it.

Answer

Summary of current state

First, let me summarize where your data is. You have a list of one or more objects named searchBoxCount. Each of the elements in the list is an object which has a name property, so you could, for example, call let name = this.searchBoxCount[0].name; to get the name of the first object in the list.

In the HTML template you use ngFor to loop through all of the objects in the searchBoxCount list, and in each iteration you assign the object to a local (to the ngFor) variable named inputSearch. You then bind the input from the textarea created in each loop iteration to the name property for that iteration's inputSearch object.

How to get your data

The key here is that the inputSearch is the same Object as is stored in searchBoxCount at some particular index (index 0 for the first object, etc...). So when the ngModel is tied to inputSearch.name it is also bout to searchBoxCount[n].name. External to the ngFor, you would loop through the searchBoxCount list to get each name you need.

As a consequence

Based on the comments on the original post, it sounds like you can have one or more names that you need to include in the query string output. That means for your getQueryString() to work, you have to loop through the list (or as in this case, let the list loop for us):

getQueryString() {
    this.isLoading = true;
    let result : string = "?id=";
    this.searchBoxCount.forEach(
        (inputSearch:any) => {  //Not the same variable, but same objects as in the ngFor
            result = result + inputSearch.name + "&id=";
    });
    result = result.slice(0, result.length - 4);  //trim off the last &id=
    return result;
}

Edit: Multiple different fields with different names

From the comments on this post, it now is clear each inputSearch has its own key to be used in the query string, that is stored in the name property. You need to preserve that name, which means you can't bind the ngModel to it. Otherwise the user will destroy the name by typing in their own text and there will be no way to get the correct key back. To that end, you need to store bind the ngModel to some other property of the inputSearch object. I am going to assume the object has a value property, so it looks like this:

{
    name: "id",
    value: "33\n44"
}

That is, each inputSearch has a name, and the value will have one or more values, separated by new line. You would then have to change the HTML template to this:

<div *ngFor="let inputSearch of searchBoxCount; let i = index" 
     [ngClass]="{'col-sm-3': swaggerParamLength=='3', 'col-sm-9': 
     swaggerParamLength=='1'}">
   <textarea name="{{inputSearch.name}}" 
             id="{{inputSearch.name}}" rows="3" class="search-area-txt" 
             attr.placeholder="Search Product {{inputSearch.name}}"
             [(ngModel)]="inputSearch?.value"></textarea>
</div>

Notice that I changed the ngModel from inputSearch.name to inputSearch?.value (the ? allows for null if there is no value to begin with). The getQueryString() method then looks something like this:

getQueryString() {
    let result:string = "?";

    //for each of the input search terms...
    this.searchBoxCount.forEach( (inputSearch:any) => {
        // first reparse the input values to individual key value pairs
        let inputValues:string = inputSearch.value.split("\n")
             .filter(function(str) { return str !== "" })
             .join("&" + inputSearch.name + "=");
        // then add it to the overall query string for all searches
        result = result + 
             inputSearch.name + 
             "=" + 
             inputValueString +
             "&"
    });
    // remove trailing '&'
    result = result.slice(0, result.length - 1);
    return result;
}

Note, using RxJs this is probably easier but I am testing vanilla javascript.

Using this, if the user entered two IDs (33 and 44), a single sku, and two emails, the result would be ?id=33&id=24&sku=abc123&email=name@compa.ny&email=an.other@compa.ny

Comments