10

Is it possible to have a similar concept of ngFor or ng-repeat in jQuery or vanilla JS?

Want to do something similar but not in angular way.

<div *ngFor="let inputSearch of searchBoxCount" class="col-sm-12">
    <textarea name="{{inputSearch.name}}" id="{{inputSearch.name}}" rows="2" class="search-area-txt" attr.placeholder="{{placeholder}} {{inputSearch.name}}">
    </textarea>
</div>

maybe use with the data="" attribute, whichever make sense.

jrbedard
  • 3,662
  • 5
  • 30
  • 34
MrNew
  • 1,384
  • 4
  • 21
  • 43
  • 1
    _but not in angular way_ yeah, why using something good when we can make it worse... – baao Dec 22 '16 at 17:53
  • Its because I'm not doing it in angular otherwise I will do it in angular. I just want to take the concept and apply it in jquery/vanilla js instead. – MrNew Dec 22 '16 at 17:58
  • you'd probably want to use a – phil294 Sep 04 '18 at 20:30
  • also see https://stackoverflow.com/questions/11128700/create-a-ul-and-fill-it-based-on-a-passed-array – phil294 Sep 04 '18 at 20:48

4 Answers4

7

If you want it in javascript you have to create elements dynamically in a loop. *ngFor and ngRepeat in angular are directives that contains template bindings that we can't have either in javascript or jquery. Once the directives encounters angular try to render the respective templates till the loop ends. Anyway, by javascript we can do like this. If you want to append 6 divs to the body element with respective id, You have to do like below.

var array = ['first', 'second', 'third', .......so on];
for(var i=0; i<array.length; i++){
   var elem = document.createElement("div");
   elem.setAttribute('id', array[i]);
   document.body.appendChild(elem);
}

We can't do it as we did in Angular with ngFor.

Chef Lax
  • 89
  • 2
  • 10
Mr_Perfect
  • 8,254
  • 11
  • 35
  • 62
6

    const anArray = [
        { tittle: "One", body: "Example 1" },
        { tittle: "Two", body: "Example 2" }
    ]
    
    function ngForFunctionality() {
        let value = '';
        anArray.forEach((post) => {
            value += `<li>${post.tittle} - ${post.body}</li>`;
        });
        document.body.innerHTML = value;
    };
    
    ngForFunctionality();
<body></body>
Manas Sahu
  • 779
  • 8
  • 8
4

In JQuery, I'd do something like this:

HTML:

<div class="searchbox-container col-sm-12">        
</div>

JavaScript:

var textAreas = $.map(searchBoxCount, function(inputSearch) {
    return $("<textarea></textarea")
      .attr({
        name: inputSearch.name,
        id: inputSearch.name
        rows: "2",
        placeholder: placeholder + " " + inputSearch.name
      })
      .addClass("search-area-txt");
});

$('.searchbox-container').append(textAreas);
StriplingWarrior
  • 151,543
  • 27
  • 246
  • 315
2

There is no easy way to get a similar ngFor by vanilla. But it's possible!

My implementation (You can make it better using more regex):

HTML code:

   <ul id="my-list">
      <li *for="let contact of contactsList">
        <span class="material-icons">{{ contact.icon }}</span>
        <span>{{ contact.value }}</span>
      </li>
    </ul>

JS code for implement *for like Angular's *ngFor:

/**
 * (*for) cicle implementation
 * @param element the root element from HTML part where you want to apply (*for) cicle. This root element cannot to use (*for). Just children are allowed to use it.
 * @returns void
 */
function htmlFor(element) {
  return replace(element, element);
}

/**
 * Recursive function for map all descendants and replace (*for) cicles with repetitions where items from array will be applied on HTML.
 * @param rootElement The root element
 * @param el The mapped root and its children
 */
function replace(rootElement, el) {
  el.childNodes.forEach((childNode) => {
    if (childNode instanceof HTMLElement) {
      const child = childNode;
      if (child.hasAttribute('*for')) {
        const operation = child.getAttribute('*for');
        const itemsCommand = /let (.*) of (.*)/.exec(operation);
        if (itemsCommand?.length === 3) {
          const listName = itemsCommand[2];
          const itemName = itemsCommand[1];

          if (rootElement[listName] && Array.isArray(rootElement[listName])) {
            for (let item of rootElement[listName]) {
              const clone = child.cloneNode(true);
              clone.removeAttribute('*for');
              const htmlParts = clone.innerHTML.split('}}');
              htmlParts.forEach((part, i, parts) => {
                const position = part.indexOf('{{');

                if (position >= 0) {
                  const pathTovalue = part
                    .substring(position + 2)
                    .replace(/ /g, '');
                  const prefix = part.substring(0, position);

                  let finalValue = '';
                  let replaced = false;

                  if (pathTovalue.indexOf('.') >= 0) {
                    const byPatternSplitted = pathTovalue.split('.');
                    if (byPatternSplitted[0] === itemName) {
                      replaced = true;
                      for (const subpath of byPatternSplitted) {
                        finalValue = item[subpath];
                      }
                    }
                  } else {
                    if (pathTovalue === itemName) {
                      replaced = true;
                      finalValue = item;
                    }
                  }
                  parts[i] = prefix + finalValue;
                }

                return part;
              });

              clone.innerHTML = htmlParts.join('');

              el.append(clone);
            }
          }
        }
        el.removeChild(child);
      }
      replace(rootElement, child);
    }
  });
}

Finally, in your component code:

document.addEventListener('DOMContentLoaded', () => {
  const rootElement = document.getElementById('my-list');

  rootElement.contactsList = [
    {
      icon: 'icon-name',
      value: 'item value here',
    },
    ...
  ];

  htmlFor(rootElement);
});

Finished. You have a *for on your vanilla code.

If anyone wants to experiment a performance comparison of this *for with the Angular's *ngFor, please share it with me, as I'm curious.

Code on stackblitz

Juninho Cruz
  • 1,158
  • 9
  • 11