মূল কনটেন্টে যান
🐹
emberjs learning
গাইড•1 · Route Loading and Data Fetching

Implement Smart Route Model Caching

impact · MEDIUM-HIGHReduce redundant API calls and improve UXplan · দিন ০৩১
আগে নিজে — এই কোডে কী ভুল, ঠিকটা কেমন হবে?

Incorrect (always fetches fresh data):

// app/routes/post.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PostRoute extends Route {
  @service store;

  model(params) {
    // Always makes API call, even if we just loaded this post
    return this.store.request({ url: `/posts/${params.post_id}` });
  }
}
পুরো rule · Correct আর ব্যাখ্যা — আগে নিজে লিখুন, তারপর খুলুন

Implement intelligent model caching strategies to reduce redundant API calls and improve user experience.

Incorrect (always fetches fresh data):

// app/routes/post.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PostRoute extends Route {
  @service store;

  model(params) {
    // Always makes API call, even if we just loaded this post
    return this.store.request({ url: `/posts/${params.post_id}` });
  }
}
// app/templates/post.gjs
<template>
  <article>
    <h1>{{@model.title}}</h1>
    <div>{{@model.content}}</div>
  </article>
  {{outlet}}
</template>

Correct (with smart caching):

// app/routes/post.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PostRoute extends Route {
  @service store;

  model(params) {
    // Check cache first
    const cached = this.store.cache.peek({
      type: 'post',
      id: params.post_id,
    });

    // Return cached if fresh (less than 5 minutes old)
    if (cached && this.isCacheFresh(cached)) {
      return cached;
    }

    // Fetch fresh data
    return this.store.request({
      url: `/posts/${params.post_id}`,
      options: { reload: true },
    });
  }

  isCacheFresh(record) {
    const cacheTime = record.meta?.cachedAt || 0;
    const fiveMinutes = 5 * 60 * 1000;
    return Date.now() - cacheTime < fiveMinutes;
  }
}
// app/templates/post.gjs
<template>
  <article>
    <h1>{{@model.title}}</h1>
    <div>{{@model.content}}</div>
  </article>
  {{outlet}}
</template>

Service-based caching layer:

// app/services/post-cache.js
import Service from '@ember/service';
import { service } from '@ember/service';
import { TrackedMap } from 'tracked-built-ins';

export default class PostCacheService extends Service {
  @service store;

  cache = new TrackedMap();
  cacheTimes = new Map();
  cacheTimeout = 5 * 60 * 1000; // 5 minutes

  async getPost(id, { forceRefresh = false } = {}) {
    const now = Date.now();
    const cacheTime = this.cacheTimes.get(id) || 0;
    const isFresh = now - cacheTime < this.cacheTimeout;

    if (!forceRefresh && isFresh && this.cache.has(id)) {
      return this.cache.get(id);
    }

    const post = await this.store.request({ url: `/posts/${id}` });

    this.cache.set(id, post);
    this.cacheTimes.set(id, now);

    return post;
  }

  invalidate(id) {
    this.cache.delete(id);
    this.cacheTimes.delete(id);
  }

  invalidateAll() {
    this.cache.clear();
    this.cacheTimes.clear();
  }
}
// app/routes/post.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PostRoute extends Route {
  @service postCache;

  model(params) {
    return this.postCache.getPost(params.post_id);
  }

  // Refresh data when returning to route
  async activate() {
    super.activate(...arguments);
    const params = this.paramsFor('post');
    await this.postCache.getPost(params.post_id, { forceRefresh: true });
  }
}
// app/templates/post.gjs
<template>
  <article>
    <h1>{{@model.title}}</h1>
    <div>{{@model.content}}</div>
  </article>
  {{outlet}}
</template>

Using query params for cache control:

// app/routes/posts.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class PostsRoute extends Route {
  @service store;

  queryParams = {
    refresh: { refreshModel: true },
  };

  model(params) {
    const options = params.refresh ? { reload: true } : { backgroundReload: true };

    return this.store.request({
      url: '/posts',
      options,
    });
  }
}
// app/templates/posts.gjs
<template>
  <div class="posts">
    <button {{on "click" (fn this.refresh)}}>
      Refresh
    </button>

    <ul>
      {{#each @model as |post|}}
        <li>{{post.title}}</li>
      {{/each}}
    </ul>
  </div>
  {{outlet}}
</template>

Background refresh pattern:

// app/routes/dashboard.js
import Route from '@ember/routing/route';
import { service } from '@ember/service';

export default class DashboardRoute extends Route {
  @service store;

  async model() {
    // Return cached data immediately
    const cached = this.store.cache.peek({ type: 'dashboard' });

    // Refresh in background
    this.store.request({
      url: '/dashboard',
      options: { backgroundReload: true },
    });

    return cached || this.store.request({ url: '/dashboard' });
  }
}
// app/templates/dashboard.gjs
<template>
  <div class="dashboard">
    <h1>Dashboard</h1>
    <div>Stats: {{@model.stats}}</div>
  </div>
  {{outlet}}
</template>

Smart caching reduces server load, improves perceived performance, and provides better offline support while keeping data fresh.

Reference: WarpDrive Caching