{"id":13701441,"url":"https://github.com/aarondfrancis/vue-model","last_synced_at":"2025-10-02T00:31:47.923Z","repository":{"id":143926338,"uuid":"59068585","full_name":"aarondfrancis/vue-model","owner":"aarondfrancis","description":"Model component for Vue.js","archived":true,"fork":false,"pushed_at":"2017-12-13T21:32:44.000Z","size":236,"stargazers_count":856,"open_issues_count":13,"forks_count":42,"subscribers_count":30,"default_branch":"master","last_synced_at":"2025-01-20T04:32:01.787Z","etag":null,"topics":["laravel","laravel55","vue","vue2","vuejs"],"latest_commit_sha":null,"homepage":null,"language":"JavaScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/aarondfrancis.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null}},"created_at":"2016-05-18T00:25:03.000Z","updated_at":"2024-11-30T13:17:43.000Z","dependencies_parsed_at":"2024-01-02T23:39:01.772Z","dependency_job_id":"278f06f7-84e3-443a-99f9-7f2976d80fb4","html_url":"https://github.com/aarondfrancis/vue-model","commit_stats":null,"previous_names":[],"tags_count":7,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aarondfrancis%2Fvue-model","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aarondfrancis%2Fvue-model/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aarondfrancis%2Fvue-model/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/aarondfrancis%2Fvue-model/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/aarondfrancis","download_url":"https://codeload.github.com/aarondfrancis/vue-model/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":234916159,"owners_count":18906643,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["laravel","laravel55","vue","vue2","vuejs"],"created_at":"2024-08-02T20:01:38.201Z","updated_at":"2025-10-02T00:31:42.611Z","avatar_url":"https://github.com/aarondfrancis.png","language":"JavaScript","funding_links":[],"categories":["JavaScript","Utilities [🔝](#readme)","Awesome Vue.js [![Awesome](https://cdn.rawgit.com/sindresorhus/awesome/d7305f38d29fed78fa85652e3a63e154dd8e8829/media/badge.svg)](https://github.com/sindresorhus/awesome)","公用事业","Components \u0026 Libraries","Utilities","Awesome Vue.js"],"sub_categories":["Libraries \u0026 Plugins","国家管理","Utilities","State Management"],"readme":"## About\nVue-model is a Javascript plugin for Vue.js that gives you the ability to transform your plain data into rich models with built-in and customizable HTTP actions, computed properties, and methods.\n\nThis project started because I work in Vue relatively often and really wanted to be able to call `customer.save()`, have it `POST` the data to the server, show the user feedback that the action was in progress, and then apply the server's results to the model.\n\nSo that's what this plugin does.\n\n## TOC\n- [Installation](#installation)\n- [Quick Examples](#quick-examples)\n    - [Create a Model](#create-a-model)\n    - [Update a Model](#update-a-model)\n    - [Fetch or Delete a Model](#fetch-or-delete-a-model)\n- [Defining Models](#defining-models)\n    - [Definition Options](#definition-options)\n        - [Attributes](#attributes)\n        - [Methods \u0026 Computed](#methods--computed)\n        - [HTTP](#http)\n            - [Action Definitions](#action-definitions)\n            - [Data](#data)\n- [Registering Models](#registering-models)\n- [Creating Models](#creating-models)\n    - [Manually](#manually)\n        - [A Single Model](#a-single-model)\n        - [Multiple Models](#multiple-models)\n    - [Automatically](#automatically)\n- [Performing HTTP Actions](#performing-http-actions)\n    - [Route Interpolation](#route-interpolation)\n    - [Applying Response Data (or Not)](#applying-response-data-or-not)\n- [Busy Indicators](#busy-indicators)\n    - [Global](#global)\n    - [Action-Specific](#action-specific)\n- [Events](#events)\n    - [Naming](#naming)\n    - [Data](#data-1)\n    - [Listeners](#listeners)\n        - [Automatically Adding Listeners](#automatically-adding-listeners)\n        - [Manually Adding Listeners](#manually-adding-listeners)\n- [Testing](#testing)\n\n## Installation\n \n```\n\u003e npm install --save vue-model\n```\n \n```javascript\nVue.use(require('vue-model'));\n```\n\n## Quick Examples\n\nHere are a few very basic examples to show you a little bit of what you can do with vue-model.\n\n### Create a Model\n\n`POST` the model to the backend and show a spinner while it's happening.\n\n```html\n\u003cbutton @click.prevent='customer.http.store()'\u003e\n    \u003ctemplate v-if='customer.http.createInProgress'\u003e\n        \u003ci class='fa fa-spinner fa-spin'\u003e\u003c/i\u003e\n        Creating...\n    \u003c/template\u003e\n    \u003ctemplate v-if='!customer.http.createInProgress'\u003e\n        Create Customer\n    \u003c/template\u003e\n\u003c/button\u003e\n```\n\n### Update a Model\n\nThe same as creating, but instead it issues a `PUT`.\n\n```html\n\u003cbutton @click.prevent='customer.http.update()'\u003e\n    \u003ctemplate v-if='customer.http.updateInProgress'\u003e\n        \u003ci class='fa fa-spinner fa-spin'\u003e\u003c/i\u003e\n        Saving...\n    \u003c/template\u003e\n    \u003ctemplate v-if='!customer.http.updateInProgress'\u003e\n        Save Customer\n    \u003c/template\u003e\n\u003c/button\u003e\n```\n\n### Fetch or Delete a Model\n\nSome of the other default actions you can execute, right out of the box.\n\n```html\n\u003cdiv v-for='customer in customers'\u003e\n    {{ customer.name }}\n    (\u003ca href='#' @click.prevent='customer.http.fetch()'\u003eRefresh\u003c/a\u003e)\n    (\u003ca href='#' @click.prevent='customer.http.destroy()'\u003eDelete\u003c/a\u003e)\n\u003c/div\u003e\n```\n\n## Defining Models\n\nDefining your models is as simple as providing a plain JSON object, but it's often better to keep them in dedicated files.\n\nHere's an example of a `customer` model that defines a few of the model's attributes, some `http` options, methods, and computed properties.\n\n```javascript\nvar Customer = module.exports = {\n    attributes: [\n        'id',\n        'name',\n        'email',\n    ],\n\n    http: {\n        baseRoute: '/api/customers/',\n        actions: {\n            // Don't expose the destroy action\n            destroy: false\n        }\n    },\n\n    methods: {\n        sayHello() {\n            alert('Hello ' + this.name);\n        }\n    },\n\n    computed: {\n        is_aaron: function () {\n            return this.email === 'aarondfrancis@gmail.com'\n        }\n    }\n}\n```\n\nOnce you create a model (which we'll talk about in a bit), you now have a super powerful object you can play with in your views.\n\nHere's a simple form that allows a user to update their name and persist it to the backend. (Bonus: it also shows a loading indicator!)\n\n```html\nChange your name:\n\u003cinput v-model='customer.name'\u003e\n\n\u003cbutton @click='customer.http.update()'\u003e\n    \u003ctemplate v-if='customer.http.updateInProgress'\u003e\n        \u003ci class='fa fa-spinner fa-spin'\u003e\u003c/i\u003e\n        Saving...\n    \u003c/template\u003e\n    \u003ctemplate v-if='!customer.http.updateInProgress'\u003e\n        Save\n    \u003c/template\u003e\n\u003c/button\u003e\n```\n\nAnd here's the same form, with feedback for errors:\n\n```html\nChange your name:\n\u003cinput v-model='customer.name' :class=\"{'error': customer.http.errors.has('name') }\"\u003e\n\n\u003cdiv v-if='customer.http.errors.any()'\u003e\n    Uh oh!\n    \u003cul\u003e\n        \u003cli v-for='error in customer.http.errors.flat()'\u003e\n            {{ error.field }}: {{ error.message }}\n        \u003c/li\u003e\n    \u003c/ul\u003e\n\u003c/div\u003e\n\n\u003cbutton @click='customer.http.update()'\u003e\n    \u003ctemplate v-if='customer.http.updateInProgress'\u003e\n        \u003ci class='fa fa-spinner fa-spin'\u003e\u003c/i\u003e\n        Saving...\n    \u003c/template\u003e\n    \u003ctemplate v-if='!customer.http.updateInProgress'\u003e\n        Save\n    \u003c/template\u003e\n\u003c/button\u003e\n```\n\nThe crazy thing about this it's all done right there with our model: putting the data, showing loading indicators, receiving the response, and mapping the validation errors!\n\n### Definition Options\n\nYour model definitions are merged with the values in `Defaults.js` to create a fully fleshed out model. Let's take a look at some of the options you can define.\n\n#### Attributes\nThe top level `attributes` key defines your model's... attributes. When a model is created, we'll default all these keys to `null` so that Vue tracks them. Just like plain objects in Vue, if you set a previously undefined key on an object, Vue may not track it and you'll run into unexpected (and frustrating) results.\n\nThe `attributes` array also forms the basis for what data is sent to the server for HTTP requests. You can of course modify what data is sent, but the starting point is the keys in the `attributes` array.\n\n#### Methods \u0026 Computed\nThis will be familiar to you if you're familiar with Vue. They work the same way.\n\n#### HTTP\nNow we're getting to the really good stuff. The top level `http` key holds all of our configuration for making requests. Let's look at some of the keys in the `http` object.\n\n`baseRoute`: This may be something like `/customers/`, or `/api/customers/`, resulting in final urls like `/customers/{id}`\n\n`eventPrefix`: The namespace for events sent from this model. If you don't define a value for this, then it is automatically set to the `name` that you register your model with. (See the Events section for more on this.)\n\n`takeAtLeast`: If you want your actions to take at _least_ a certain amount of time, you can set this value to something greater than 0. Sometimes if an action takes \u003c 100ms, it can seem like it didn't work. I learned this from watching an Adam Wathan live-stream, and then later saw it on [his twitter](https://twitter.com/adamwathan/status/885130802513752065). By default it's set to 0ms.\n\n`axios`: a function that lets you customize the axios request configuration option, if you want.\n\n`getDataFromResponse`: this is the function that takes the axios response from the server and turns it into a key-value map of data. This just depends on how your backend returns the data. You may return it in a nested `data` key, or you may return it with no nesting at all.\n\n`getErrorsFromResponse`: same idea as `getDataFromResponse`, but for validation errors. This function takes the `response` and returns a map where keys are field names, and values are **arrays** of errors. By default this is set up for Laravel 5.5 by returning `response.data.errors`.\n\n`isValidationError`: there are lots of things that can go wrong with a request, but we only want to try to map validation errors when the thing that went wrong was validation. This function receives an error and returns a bool if it was a validation error. By default, this is set up to return `true` if the response code is `422`, which is what Laravel sends when validation fails.\n\n`errorKey`: by default, we nest the errors on the `http` key, making access from your model look something like `customer.http.errors.{method}`. If you don't like that, you can change it! You can set this value to e.g. `errors`, and it will live at the top of your model, making access a little easier: `customer.errors.{method}`. (Make sure your model doesn't have an `errors` attribute, method, or computed property though!)\n\n##### Action Definitions\n\n`actionDefaults` the defaults that are applied to every action:\n\n```javascript\n{\n    // Apply the response data to the model\n    apply: false,\n\n    // The server validates this request and could\n    // return a validation error in response\n    validation: true,\n\n    // Modify what data we should send to the server\n    data: {\n        // only: [],\n        // with: [],\n        // without: [],\n        // custom: function(data, definition) {\n        //     return data;\n        // }\n    }\n}\n```\n\nThe actions live in the `actions` key. This defines the http actions available on your model. You can simply use the defaults, or make up your own! By default, we have `index`, `store`, `fetch`, `update`, and `destroy`.\n\nIf you wanted to make your own, you could do so like this:\n\n```javascript\n{\n    actions: {\n        favorite: {\n            method: 'PUT',\n            route: '{id}/favorite',\n            apply: true,\n            data: false\n        },\n        unfavorite: {\n            method: 'DELETE',\n            route: '{id}/favorite',\n            apply: true,\n            data: false\n        }\n    }\n}\n```\n\nNow you have two new methods on your model, `favorite` and `unfavorite`, which you'd call by e.g.: `customer.http.favorite()`. (Why you would favorite a customer is beyond me, but you get the idea.)\n\nAssuming your `baseRoute` is `/customers/` and its `id` is 1, the favorite action will `PUT` to `/customers/1/favorite` with no data (`data`: false), and apply whatever response comes back from the server. (The server could respond with `{favorited: true}` for example.)\n\nIf you want to disable one of the default actions, just set the value to `false`\n\n```javascript\n{\n    actions: {\n        destroy: false\n    }\n}\n```\n\n`customer.http.destroy` will be undefined now.\n\n##### Data\n\nBy default, we'll send all the keys + values from your model's `attributes` array. This may not be ideal in many cases though, so you can modify what data gets sent for each action. (Some of these examples may be contrived, but bear with me...)\n\n- `false` means no data will be sent at all\n    ```javascript\n    {\n        favorite: {\n            data: false\n        }\n    }\n    ```\n\n- `only: []` defines the **only** keys from the `attributes` you want sent.\n    ```javascript\n    {\n        setName: {\n            data: {\n                only: ['name']\n            }\n        }\n    }\n    ```\n\n- `without: []` defines which keys you want to **exclude**\n    ```javascript\n    {\n        update: {\n            data: {\n                // Maybe this a separate resource?\n                without: ['favorited']\n            }\n        }\n    }\n    ```\n\n- `with: []` keys present on `this` but not in `attributes` that you want to include, perhaps a computed property or method\n    ```javascript\n    {\n        update: {\n            data: {\n                with: ['computed_property', 'method']\n            }\n        }\n    }\n    ```\n\n- `custom: function` after all of the above are computed, as the very last step we'll check for a `custom` function. The function receives the `payload` we're about send as well as the action's definition. **You must return** a payload, otherwise nothing will be sent.\n    ```javascript\n    {\n        recent: {\n            data: {\n                custom: function(payload, definition) {\n                    payload.lastFetched = localStorage.get('last_fetched');\n                    return payload;\n                }\n            }\n        }\n    }\n    ```\n\n`only`, `with`, and `without` can be arrays, or functions that return arrays.\n\nThe arrays themselves can be full of strings, which are accessible from the model (attributes, methods, computed properties) or they can be plain objects.\n\nFor example:\n\n```javascript\n{\n    favorited: {\n        route: '',\n        method: 'GET',\n        data: {\n            only: [],\n            with: [{\n                active: 1,\n                favorited: 1\n            }]\n        }\n    }\n}\n```\n\nThat action would send a `GET` to `/customers/?active=1\u0026favorited=1`. We dropped every key via the `only:[]`, and then added some static data via `with`.\n\n## Registering Models\n\nNow that we know alllll about model definition, let's talk about registration. The registration process is simple using the `Vue.models.register` method, eg:\n\n```javascript\nvar VueModel = require('vue-model');\nVue.use(VueModel);\n\nVue.models.register('customer', {\n    // model definition\n});\n\nVue.models.register('foo', {\n    // model definition\n});\n```\n\nThe first argument is the `name` argument, the second argument is a plain object that lets you define some options that are specific to your model.\n\nHere's an example of registering a `customer` model that has a base route of `/customers`:\n```javascript\nVue.models.register('customer', {\n    http: {\n        baseRoute: '/customers'\n    }\n});\n```\n\nYou can also register many at once during the plugin install:\n\n```javascript\nvar VueModel = require('vue-model');\nVue.use(VueModel, {\n    customer: require('./models/customer'),\n    foo: require('./models/foo'),\n    bar: require('./models/bar'),\n});\n```\n\nIn this case, they key is the `name` and the value is the model's definition object.\n\nNow you're ready to start creating and using your models.\n\n## Creating Models\n\nThere are two different ways to create models in vue-model: You can create them manually whenever you please, or you can have vue-model create them automatically.\n\n### Manually\n\n#### A Single Model\nTo manually create a model, use the `$model()` Vue Instance method. \n  \nWithin a Vue Instance: \n\n```javascript\nthis.$model(name, data, options);\n```\n\nThe `$model()` method accepts 3 parameters:\n- `name`: (string) The type of model. This is the same key you used to register the model\n- `data`: (object) The model data\n- `options`: (object) Any _instance_ specific model definitions\n\nYou can create the model wherever you please. For example, you can call the method inside the `data` function: \n\n```javascript\nnew Vue({\n    el: '#app',\n    \n    data: function() {\n        return {\n            customer: this.$model('customer', {\n                name: 'Aaron'\n            })\n        };\n    }\n});\n```\n\nOr you can call it anywhere else! Here's an example where we instantiate a model within Vue's `created` lifecycle hook.\n```javascript\nnew Vue({\n    el: '#app',\n    \n    data: {\n        customer: {\n            name: 'Aaron'\n        }\n    },\n    \n    created: function() {\n        this.customer = this.$model('customer', this.customer);\n    }\n});\n```\n#### Multiple Models\nDo you have a whole set of models that need to be created? Great! Just use the `this.$models` method. It's the same as `this.$model` except the data parameter is an array of objects.\n\n- `name`: (string) The type of model. This is the same key you used to register the model\n- `data`: (array) A collection model data\n- `options`: (object) Any _instance_ specific model definitions\n\nThis will return an array of models.\n\n\u003e You can definitely do this yourself using a `for` loop and the `this.$model` method, `this.$models` is just a little more convenient.\n\n### Automatically\nManually creating models gives you ultimate flexibility, but sometimes you just want it to work right away. That's where automatic model creation comes into play.\n \nTo automatically create models, you simply need to add a `models` array to your Vue Instance. A `models` array element can take two forms. The first form is just a string: \n\n```javascript\nnew Vue({\n    el: '#app',\n    \n    models: ['customer'],\n    \n    data: {\n        customer: {\n            name: 'Aaron'\n        }\n    }\n});\n```\n\nWhen you pass a string in, the **model type the data key must be the same**. In the example above, the model type must be `customer`, and the data key must also be `customer`.\n \nIf you need more flexibility in naming, you can pass in a proper object.\n\n```javascript\nnew Vue({\n    el: 'body',\n\n    models: [{\n        type: 'customer'\n        key: 'newCustomer'\n    }],\n\n    data: {\n        newCustomer: {\n            name: 'Aaron'\n        }\n    }\n});\n```\n \nIn this example, the model type is still `customer`, but the actual data lives on the data key `newCustomer`. \n \n\u003e Under the hood, vue-model adds a mixin that latches on to the `created` lifecycle event to create models automatically. [Read more about the Vue Instance lifecycle](https://vuejs.org/guide/instance.html#Lifecycle-Diagram)\n\nDepending on what the data is will depend on whether one or many models are automatically created. If the data is an array, many model will be created. For example:\n\n```javascript\nnew Vue({\n    el: '#app',\n\n    models: [{\n        type: 'customer'\n        key: 'customers'\n    }],\n\n    data: {\n        customers: [{\n            name: 'Aaron'\n        },{\n            name: 'Evan'\n        }]\n    }\n});\n```\n\nWe told vue-model to automatically create the `customer` model for the data that lives at the `customers` key. Because the data at `customers` is an array, vue-model will loop through and make each item a model.\n \n\n## Performing HTTP Actions\n\nPerforming HTTP Actions is the heart of vue-model. The whole purpose of this plugin is to make it painless for your models to interact with your application's backend. \n \nAll the actions are available on the `http` key. To perform an action, you just need to call the corresponding method.\n\nExamples:\n\n```javascript\n// Create a new customer\ncustomer.http.store();\n\n// Fetch this customer from the server\ncustomer.http.fetch();\n\n// Save this customer\ncustomer.http.update();\n\n// Delete this customer\ncustomer.http.destroy();\n\n// Retrieve a list of customers\ncustomer.http.index()\n```\n\nThese are the 5 actions that vue-model ships with, but you are welcome to disable those and/or set up your own.\n\n### Route Interpolation\n\nAll of your action's routes will be interpolated with your model's data. So if your model has an `id` of `10`, a route of \n\n```\n/videos/{id}\n```\n\nbecomes\n\n```\n/videos/10\n```\n\nYou can do this with any attribute from your model. If your model's `type` has a value of `watched`, a route defined as \n\n```\n/videos/{type}/increment\n```\n\nwould become\n\n```\n/videos/watched/increment\n```\n\n\n### Applying Response Data (or Not) \n\nAnother great thing about vue-model is that you can automatically update your models with the response that comes back from the server.\n\nIf you define your action with `apply = true`, vue-model will take the response from the server, loop through all the data and set the values.\n\n```javascript\n{\n    favorite: {\n        method: 'PUT',\n        route: '{id}/favorite',\n        apply: true,\n        data: false\n    }\n}\n```\n\nIf the server returns \n\n```javascript\n{\n    favorited: 1\n}\n```\n\nas its payload from the `complete` action, then the `completed` attribute on our model will automatically be updated. \n\n```javascript\ncustomer.http.favorite();\n// Once it finishes...\nconsole.log(customer.favorited);\n// \u003e 1\n```\n\nThat lets us create toggle buttons very easily, all in HTML.\n\n```html\n\u003cbutton v-if=\"customer.favorited\" @click.prevent=\"video.$.unfavorite()\"\u003e\n    Favorited\n\u003c/button\u003e\n\n\u003cbutton v-if=\"!customer.favorited\" @click.prevent=\"video.$.favorite()\"\u003e\n    Mark as Favorite\n\u003c/button\u003e\n```\n\n\n## Busy Indicators\n\nYou'll often want to know when the model is busy, so that you can show loading indicators or prevent other actions. Vue-model provides two types of busy indicators: Global, and Action Specific.\n\n### Global\nThe global busy indicator lives in the http object under the `inProgress` key.\n\nFor example, if you have a model named `customer`, you can observe the `customer.http.inProgress` attribute. This is helpful for showing/hiding elements or disabling buttons.\n\nHere's one way you can disable a button, should the model be busy performing an HTTP action:\n\n```html\n\u003cbutton @click='customer.http.complete()' :disabled='customer.http.inProgress'\u003e\n    Mark as Favorite\n\u003c/button\u003e\n```\n\n### Action-Specific\n\nIf you have loading indicators scattered across the page and only want to show the correct indicator based on the specific action, then you should use an action-specific busy indicator.\n\nFor every action, there is a corresponding property that indicates whether or not that action is currently in process. For example, if the action is named `update`, then the property would be named `updateInProgress`. \n\nConsider a case where you have a `complete` action for a `video` model and would like to show a loading indicator on the button.\n\n```html\n\u003cbutton @click='video.http.complete()' :disabled='video.http.completeInProgress'\u003e\n    \u003ci v-if='video.http.completeInProgress' class='fa fa-spinner fa-spin'\u003e\u003c/i\u003e\n    Mark as Complete\n\u003c/button\u003e\n```\n\nThis button will disable itself and show the lovely Font Awesome loading indicator while the model finishes the `complete` action. This provides feedback and a good experience for your users. However, in this example if a different action is being performed, say a `favorite` action, the button will not show the loading indicator because it is bound to `completeInProgress` and not `inProgress` or `favoriteInProgress`.\n\n\u003e When _any_ of the action-specific loading indicators (`{action}InProgress`) are `true`, the global `inProgress` indicator will also be `true`.\n\n## Events\n\nVue-model emits several events that you can listen for and respond to, giving you many different ways to seamlessly tie your app into vue-model.  \n\n### Naming\n\nVue-model events follow a naming scheme of `{eventPrefix}.{action}.{result}`. The `eventPrefix` can be set in your model definition.\n\nBy default, if you don't pass in an `eventPrefix` while registering your model, vue-model will set it to the `name` of model you register.\n\n```javascript\n\n// No eventPrefix, model type is 'customer'\nVue.models.register('customer', {});\n\n// --\u003e eventPrefix is equal to 'customer'\n\n\n// Explicit eventPrefix passed in\nVue.models.register('customer', {\n    http: {\n        eventPrefix: 'cst'\n    }\n});\n\n// --\u003e eventPrefix is equal to 'cst'\n```\n\n`{action}` is always equal to the name of the action on your API. If you call `customer.http.update()`, `action` will be equal to `update`.\n\n`{result}` is one of the following:\n\n- `before` - Before the action takes place\n- `success` - Successful completion of the action\n- `complete` - Action *finished*, regardless of outcome\n- `error` - Action failed\n\nPutting it all together, the event name will look similar to the following examples:\n\n- `customer.update.before`\n- `customer.destroy.success`\n- `customer.list.complete`\n- `customer.fetch.error`\n\n### Data\n\nEach event comes with `data` payload:\n\n- `{eventPrefix}.{action}.before` - Before the action takes place. Receives a plain object with a function you can call to prevent the action from happening.\n    \n    ```javascript\n    {\n        cancel: fn\n    }\n    ```\n\n- `{eventPrefix}.{action}.success` - Successful completion of the action. Receives the `response` from the server.\n\n- `{eventPrefix}.{action}.error` - Action failed. Receives the axios `error` object.\n\n\n- `{eventPrefix}.{action}.complete` - Action *finished*, regardless of outcome. No data. (Not fired if the action was cancelled manually by calling the `cancel` function in the `before` event.)\n\n### Listeners\n\n#### Automatically Adding Listeners\n\nEvents aren't much good without event listeners. To add event listeners, define a `listeners` key in your `models` option. Any model registration then moves to a `register` key. E.g:\n\n```javascript\nnew Vue({\n    el: '#app',\n\n    models: {\n        register: ['customer']\n        listeners: [\n            'customer.update.success'\n        ]\n    },\n});\n```\n\n**When you add listeners automatically like this, they are bound during the Vue instance's `mounted` hook and automatically destroyed in the `beforeDestroy` lifecycle hook.**\n\nThere are a couple different ways to register listeners within the `listeners` array. The first is a simple string, e.g. `customer.update.success`. This serves as **both the event and the handler**. Vue-model will try to call a method named `customer.update.success`.\n\nIn that case, you'd set your Vue instance up like this:\n\n```javascript\nnew Vue({\n    el: '#app',\n\n    models: {\n        register: ['customer']\n        listeners: [\n            'customer.update.success'\n        ]\n    },\n\n    methods: {\n        'customer.update.success': function() {\n            console.log('great success!')\n        }\n    }\n});\n```\n\nIf you don't like that, then you can add listeners as a map, with the key being the event and the value being the method to call, either a string or a function.\n\n```javascript\nnew Vue({\n    el: '#app',\n\n    models: {\n        register: ['customer']\n        listeners: [{\n            'customer.update.success': 'customerUpdated',\n            'customer.update.error': function() {\n                console.log('oh no!');\n            }\n        }]\n    },\n\n    methods: {\n        customerUpdated: function() {\n            console.log('great success!')\n        }\n    }\n});\n```\n\nYou can mix and match however you please. If you want to map multiple handlers to a single event, you're free to just keep adding elements to the array.\n\n```javascript\nnew Vue({\n    el: '#app',\n\n    models: {\n        register: ['customer']\n        listeners: [\n            'customer.update.success',\n        {\n            'customer.update.error': function() {\n                console.log('oh no!');\n            }\n        }, {\n            'customer.update.error': function() {\n                console.log('two handlers for the same event, why not');\n            }\n        }]\n    },\n\n    methods: {\n        'customer.update.success': function() {\n            console.log('great success!')\n        }\n    }\n});\n```\n\n\n#### Manually Adding Listeners\n\nThere may be times when you don't want to add listeners when your instance is mounted, which is when the automatic listeners are bound. If that's the case, you're in luck because you can use the `$addModelListeners` and `$removeModelListeners` methods.\n\n\n```javascript\nnew Vue({\n    el: '#app',\n\n    // No automatic listeners\n    models: {\n        register: ['customer']\n    },\n\n    watch: {\n        // Some property that determines if this\n        // component is \"active\"\n        componentIsActive: function(val) {\n            var method = val ? '$add' : '$remove';\n\n            // Add or remove our listeners\n            this[method + 'ModelListeners']([\n                'customer.update.success'\n            ]);\n        }\n    }\n\n    methods: {\n        'customer.update.success': function() {\n            console.log('great success!')\n        }\n    }\n});\n```\n\n\n**Remember:** When you don't use automatic event listeners, you are responsible for cleaning up your event listeners.\n\n## Testing\n\n- Run `npm t` or `npm test` to run all tests once.\n- Run `npm run test:watch` to watch and rerun tests.\n\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faarondfrancis%2Fvue-model","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Faarondfrancis%2Fvue-model","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Faarondfrancis%2Fvue-model/lists"}