0

With SPA app growth, some things become unclear with Angular JS.

Suppose, we have a RDBMS data structure with lots of referencing entities, like Country (id), State (id, countryId), City (id, stateId), Street (id, cityId).

And we need to render most of it within a single view, just like a street index grouped by parent recursively. I know it's not so practical example, especially considering that our real application entities have mutual data that we need to bind to.

How would "Best Practice" look like?

Now, we're using lots of ng-repeat's like this:

<ul>
    <li ng-repeat="country in countryList">
        {{ country.name }}
        <ul ng-if="(stateList | filter:{countryId === country.id}).length">
             <li ng-repeat="state in filterStatesByCountryId(country.id)">
             {{ state.name }}
                 <ul ng-if="(cityList | filter:{stateId === state.id}).length">
                     <li ng-repeat="city in cityList | filter:{stateId === state.id}">{{ city.name }}</li>
                 </ul>
             </li>
        </ul>
    </li>
</li>

filterStatesByCountryId(country.id) is the same as stateList | filter:{countryId === country.id}.

Our app must have bindings in place, so that if street.name property gets updated in the StreetService, the view reflects this change. Therefore $scope.streetList = StreetService.all is not an option right out of the box and instead of $scope.streetList we would have $scope.street = StreetService and then use it as $scope.street.all | filter:{cityId: city.id}.

Must say this looks scary.

What would be the most optimal both performance and service architecture wise way of rendering and displaying this kind of lists of data?

One of idea would be to store whole merged hierarchy of entities into one. So that country with all states, cities and streets becomes one object. Then country entity would have states {Array.<StateEntity>} property, state {StateEntity} would have cities {Array.<CityEnity>} prop, etc. But then if a single street.name would get updated, we would need to traverse whole country data object to find what street we need to update, do the update and then re-render the view, or just have a $scope.watch(expr, fn, true) which would be an overkill for that complex hierarchy.

Thanks.

P.S. We cannot switch to NoSQL for storing data.

1 Answer 1

1

Angular is not really built to deal with relational data structures I can see that what you're trying to do here mimicks C# MVC templating for loops.

The thing is let's say you have: - 100 countries in your list => 100 watchers - 20-ish states per country => 2000-ish watchers - 30-ish cities per country => 60 000 watchers

IF you would show everything on one screen, now that is not what you're doing of course, but keep that in mind.

As an initial performance improvement I would add a track by to each ng-repeat statement.

 <li ng-repeat="state in filterStatesByCountryId(country.id) track by country.id">

From the angular documentation it states:

If you are working with objects that have an identifier property, you can track by the identifier instead of the whole object. Should you reload your data later, ngRepeat will not have to rebuild the DOM elements for items it has already rendered, even if the JavaScript objects in the collection have been substituted for new ones

and also

Note: track by must always be the last expression

Now, ng-repeat can be a bit sluggish in performance, I would not 2-way databind the view properties:

{{ city.name }} => {{ ::city.name }}

This could give you a significant speed increase in general, because angular won't bind watchers to those fields.

To solve your base problem however, I would model it as a singular JSON dataset. However you don't want to go through the hassle of mutating your dataset to work with Angular. At least I wouldn't, so what I would go for just a simple one-way databinding in this case. I don't know how often a street name is going to change on the fly.

It would look something like this:

 <li ng-repeat="country in ::countryList track by country.id">

Also maybe try using controllerAs syntax, helps for cleaner code.

However if you really bump into speed issues with ng-repeat it's always best to write your own version of it tailored to your use-case.

Sign up to request clarification or add additional context in comments.

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.