npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

vue-object-merge

v1.0.0

Published

Utility function for merging an object into a reactive object in Vue

Downloads

1,414

Readme

vue-object-merge

Utility function for merging an object into a reactive object in Vue

Purpose

This library was designed to efficiently and automatically deep-merge objects with Vue-managed objects.

I really enjoy Vue and Vuex's philosophy -- update your state, and your UI will reactively update automatically. But what I don't enjoy is writing code to wire API JSON responses back into my Vue data or Vuex state objects (i.e., mutation functions):

state: {
	customer {
		firstName: "",
		lastName: ""
		// ...
	}
},
actions: {
	getCustomer: id => axios
		.get("/api/getCustomer/" + id)
		.then(function(response) {
			context.commit("COMMIT_CUSTOMER", response.data)
		})
},
mutations: {
	COMMIT_CUSTOMER(state, data) {
		state.customer.firstName = data.firstName;
		state.customer.lastName = data.lastName;
		// ... sigh ...
	},
}

(The examples here are specific to Vuex, but apply equally if you're just using the data object to store your state.)

If an object is replaced entirely, all you have to do is replace one object with another. But if the API needs to return partial object changes (perhaps even changes across several portions of the page state, such as updating a table and a page header and three menu options), you have to manually wire the bits and pieces. Worse, if each API call can return different portions of state, you'll be writing a LOT of boilerplate code.

Vue-Object-Merge provides a single function, stateMerge, that performs a deep merge of one object into another. Basically, it greatly simplifies mapping keys from object A into Vue object B, without disturbing keys in B that are not included in A:

mutations: {
	// I can call this single mutation for any API response that sparely
	// matches anything in the Vuex state.
	MERGE_STATE: (state, response) => stateMerge(state, response),
}

Now, many of my Vuex actions call their API endpoint and all commit a single mutation (as above). This MERGE_STATE mutation folds the keys and values returned from the API into the application state in one step.

The API need not return a full representation of the state. For example, if an API call needs to update state.ui.userForm.fields, the API can just return the contents of the fields object (or any portion thereof) and your action can look like this:

context.commit("MERGE_STATE", { ui: { userForm: { fields: response.data } } })

This will navigate stateMerge directly to the place in your state where the response data should be merged, leaving the rest of your state unaffected (and even any keys of ui.userForm.fields that aren't part of the JSON response).

In addition to API calls, it can also be used for, say, updating data elements that are two-way-bound to form fields to set them to a Vuex state object, or vice versa to merge changes from the form back into the Vuex state.

Function definition

stateMerge(state, value, propName, ignoreNull)

where:

  • state is the object to be updated.
  • value is the object or value containing the change(s).
  • propName is the key of state to be modified (required if value isn't an object, otherwise optional). This was primarily intended for internal use for recursion, but can be handy in other situations.
  • ignoreNull should be set to true (default is false) if a null value should not overwrite state's value.

Basic Logic

Given objects state and value, stateMerge will:

  • Traverse the attributes of value
  • If the attribute is a primitive (number, boolean, etc.) or built-in object (array, date, regex, etc.), it will overwrite state's attribute with the new value (adding the attribute if it doesn't exist).
  • If the attribute is a custom object (normal JavaScript associative array), it will recurse into that object's attributes with the same logic.
  • If the attribute is null, it will decide what to do based on the ignoreNull argument.

The ignoreNull option was added to make it easier to use the same server-side object (.NET "POCOs" in my case) to service different requests, where portions of the response object not modified are set to null. This has a small data overhead over bespoke responses for each API endpoint, but encourages code reuse and consistency.

Technical implementation note: testing the "type" of a value can be complicated--typeof, instanceof, and other methods all have pros and cons. In this case, there was a simple answer: Object.prototype.toString.call(value). This returns the string [object Object] for all user-defined objects (the ones we want to recurse into), and returns various other values for built-in JavaScript types like Date, Array, RegEx, Number, String, Boolean, Math, Function, null, and undefined--the keys we usually want to overwrite.

Caveats

  • This only works if your value object forms a directed acyclic graph, otherwise you'll have an endless loop when updating.
  • This overwrites arrays, it does not merge them. It only merges objects.

Demo

Here's a CodePen where you can play with merging an object into a sample Vuex state: https://codepen.io/richardtallent/pen/eyWKGN

Examples

Basic example:

var a = { foo: 1, bar: 0 }
var b = { foo: 2, fizz: 4, fee: null }
stateMerge(a, b)
console.log(a)
// { foo: 2, bar: 0, fizz: 4, fee: null }

With the ignoreNull option:

var a = { foo: 1, fee: { id: 1 } }
var b = { foo: 2, fee: null }
stateMerge(a, b, null, true)
console.log(a)
// { foo: 2, fee: { id: 1 } }

Example of a deeper merge:

var a = { foo: [0, 1], bar: { "1": "Marcia", "2": "Peter" } }
var b = { foo: [2, 3], bar: { "1": "Jan" } }
stateMerge(a, b)
console.log(a)
// { foo: [2, 3], bar: { "1" : "Jan", "2": "Peter" } }

Using the optional propName parameter:

var a = { foo: [0, 1], bar: { "1": "Marcia", "2": "Peter" } }
var b = { "1": "Jan" }
stateMerge(a, b, "bar")
console.log(a)
// { foo: [0, 1], bar: { "1" : "Jan", "2": "Peter" } }

Vuex Example (assumes you've installed it from npm, etc.)

import { stateMerge } from "vue-object-merge"

export const store = new Vuex.Store({
...
  actions: {
	getOrders(context)) {
		return HTTP.get("/orders")
			.then(function(response)) {
				context.commit("MERGE_STATE", response.data)
			})
	}
  },
  mutations: {
	MERGE_STATE(state, data) {
		stateMerge(state, data)
	}
}

Release History

| Date | Version | Notes | | ---------- | ------- | ---------------------------------------- | | 2018.01.01 | 0.1.0 | First release | | 2018.01.01 | 0.1.1 | Cleaned up and simplified type checking. | | 2018.01.01 | 0.1.2 | Fixed module export | | 2018.01.03 | 0.1.3 | IE11 doesn't like for(const...) | | 2018.01.11 | 0.1.4 | npm build doesn't like ES6 at all | | 2018.01.15 | 0.1.5 | Added ignoreNull parameter | | 2018.11.19 | 0.1.6 | Add proper ESM/CJS, update Vue dep | | 2018.12.18 | 0.1.7 | remove =>, IE11 issue (#2) | | 2020.02.18 | 0.1.8 | Fix null state prop issue (#5) | | 2021.02.02 | 1.0.0 | BREAKING. For Vue 3. |