Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Wondering how to achieve something similar to *ngFor/ng-repeat in jquery or JS

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.

like image 677
MrNew Avatar asked Dec 22 '16 17:12

MrNew


People also ask

What is the difference between ngFor and Ng-repeat?

ng-repeat is for Angular 1. x And *ngFor is for Angular 2. The major difference between ng-repeat and *ngFor is its syntax.

Can we use ngFor in JavaScript?

You can also get the index of the element using the *ngFor directive, similar to the forEach() loop in JavaScript. All you have to do is add a new variable in the *ngFor directive. In the above snippet, a new variable, i , is added to get the index.

Which is the correct statement to fetch the index value in * ngFor?

Declare a variable inside *ngFor directive using let or as keyword. for instance say indexofelement or simply i . Assign the variable value to index .

Which directive is used to display the same set of 10 records in a page?

The ng-repeat directive repeats a set of HTML, a given number of times. The set of HTML will be repeated once per item in a collection.


1 Answers

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

like image 197
Juninho Cruz Avatar answered Sep 19 '22 13:09

Juninho Cruz