Be part of JetBrains PHPverse 2026 on June 9 – a free online event bringing PHP devs worldwide together.

rhand's avatar
Level 6

Show part of body string in Vue 3 component loading articles

I am using the following to show only part of the post on the index page with articles in a Vue 3 component:

<p class="mb-2">{{ article.body.slice(0,300) }}...</p>

Now I wonder. Is this an okay way to do this or should I do it with computed properties perhaps or a single method?

0 likes
8 replies
LaryAI's avatar
Level 58

Using a computed property would be a better approach as it allows for more flexibility and reusability. Here's an example of how to implement it:

<template>
  <div>
    <p class="mb-2">{{ truncatedBody }}...</p>
  </div>
</template>

<script>
export default {
  props: {
    article: {
      type: Object,
      required: true
    }
  },
  computed: {
    truncatedBody() {
      return this.article.body.slice(0, 300);
    }
  }
};
</script>

In this example, we define a computed property called truncatedBody that returns the first 300 characters of the article.body string. We then use this computed property in the template to display the truncated body text.

By using a computed property, we can easily reuse this logic in other components that need to display truncated text. Additionally, it keeps the template cleaner and easier to read.

rhand's avatar
Level 6

I am using the Composition API so that does not work

[Error] Unhandled Promise Rejection: TypeError: Importing a module script failed.
(anonymous function) (@inertiajs_vue3.js:4129:219)

see

<script setup>
import { ref, computed } from 'vue';
import { Head, Link } from '@inertiajs/vue3';
import {useForm } from '@inertiajs/vue3'

defineProps({
    canLogin: Boolean,
    canRegister: Boolean,
    laravelVersion: String,
    phpVersion: String,
    articles: Object,
    // randomNumber:''
})

computed: {
    truncatedBody() {
      return this.article.body.slice(0, 300);
    }
  }
...

I am trying something like

const slidedBody = computed(() => usePage().props.article.body.slice(0, 300));

based on Jetstream code but then I get

[Error] Unhandled Promise Rejection: ReferenceError: Can't find variable: usePage
(anonymous function) (@inertiajs_vue3.js:4129:219)
secondman's avatar

@rhand

Don't forget you need to import usePage

import { useForm, usePage } from '@inertiajs/vue3'
1 like
rhand's avatar
Level 6

@secondman true, that. Did a bit later on. Then realized I needed to use another method. But it did solve that general error. Thanks.

rhand's avatar
Level 6
...
const props = defineProps({
    canLogin: Boolean,
    canRegister: Boolean,
    laravelVersion: String,
    phpVersion: String,
    articles: Object,
    // randomNumber:''
})

const slidedBody = computed(() => props.articles.body.slice(0, 300));
...

does work but then it loads pretty much all

[ { "id": 1, "title": "Laravel Jetstream with Inertia and Vue", "body": "Laravel Jetstream is a popular package for building robust web applications with Laravel, a PHP web framework. It provides pre-built authentication and authorization scaffolding, team management, and more. Jetstream has recently added Inertia as an alternative front-end stack, which offers a seamless development experience and improved performance.\nInertia is a modern approach to building single-page applications (SPAs) that combines the best of both server-side rendering and client-side rendering. It allows developers to write their frontend code using a popular JavaScript framework such as Vue, React, or Svelte, and still leverage the server-side capabilities of Laravel.", "user_id": 11, "created_at": "2023-02-26T09:12:36.000000Z", "updated_at": "2023-04-26T03:57:21.000000Z" }, { "id": 2,
...

so not ideal..

PovilasKorop's avatar

I would personally have a special Excerpt column in the DB, auto-filled with Observer default to 300 characters but editable. That way you're in control on the back-end what to show for each article.

And then in the Vue you would just select that field and not full body, also saving bandwidth and downloading less data from the server.

1 like
rhand's avatar
Level 6

@PovilasKorop Does not explain how to add the computed() property properly but does offer a good alternative. Adding an additional column for excerpts would be useful. And option to add text to it would be nice besides it auto loading some data from article body.

How would you do the auto-filling of the excerpt column based on x words of the body column however?

Guess I would need to update excerpt on updating the body

...
public function update(Request $request, Article $article)
    {
        $article->update([
            // 'title' => $request->input('title'),
            'body' => $request->input('body'),
        ]);

        // return Redirect::back()->with('success', 'Article updated successfully!');
        return to_route('articles.show', array('article' => $article));
    }
...

and add something like

'excerpt' => Str::words($request->input('body'), 10, ''),

as discussed at https://laracasts.com/discuss/channels/laravel/how-to-get-a-excerpt-from-the-input-data-from-the-body . Will do some tests today.

rhand's avatar
rhand
OP
Best Answer
Level 6

Made excerpt fillable and did get excerpt added on body save. But using

<article class="max-w-4xl text-gray-600 dark:text-gray-400 text-sm" >
    <p class="mb-2">{{ article.excerpt }}...  <!-- article.body.slice(0,300) or slicedBody --></p>
</article>

I get data loaded with html as strings again. So used

<p class="mb-2" v-html="article.excerpt"></p>

Only wasn't sure how to add ... to it. Could not add it in the paragraph tag because the vhtml overrides it all. But in the end I updated the method I the controller

...
'excerpt' => Str::words($request->input('body'), 10, '') . '...',
...

and added dots there.

1 like

Please or to participate in this conversation.