link2740 link2741 link2742 link2743 link2744 link2745 link2746 link2747 link2748 link2749 link2750 link2751 link2752 link2753 link2754 link2755 link2756 link2757 link2758 link2759 link2760 link2761 link2762 link2763 link2764 link2765 link2766 link2767 link2768 link2769 link2770 link2771 link2772 link2773 link2774 link2775 link2776 link2777 link2778 link2779 link2780 link2781 link2782 link2783 link2784 link2785 link2786 link2787 link2788 link2789 link2790 link2791 link2792 link2793 link2794 link2795 link2796 link2797 link2798 link2799 link2800 link2801 link2802 link2803 link2804 link2805 link2806 link2807 link2808 link2809 link2810 link2811 link2812 link2813 link2814 link2815 link2816 link2817 link2818 link2819 link2820 link2821 link2822 link2823 link2824 link2825 link2826 link2827 link2828 link2829 link2830 link2831 link2832 link2833 link2834 link2835 link2836 link2837 link2838 link2839 link2840 link2841 link2842 link2843 link2844 link2845 link2846 link2847 link2848 link2849 link2850 link2851 link2852 link2853 link2854 link2855 link2856 link2857 link2858 link2859 link2860 link2861 link2862 link2863 link2864 link2865 link2866 link2867 link2868 link2869 link2870 link2871 link2872 link2873 link2874 link2875 link2876

[Vue.js] How to change button to disabled once it is clicked in Vue JS app Subscribe to RSS

I wanted to know how do I change a button to disabled once it is clicked, but the data comes from an rest api, so to the button disabled only on that list item and not all buttons. I can’t see where to do this in VueJS.

My component code is:

<template>
<div class=”container search”>
<List :List=”List”/>
<!– <div class=’div’ v-bind:class=”[isActive ? ‘red’ : ‘blue’]“ @click=”toggleClass()”></div> –>

<div class=”jumbotron”>
<h1 class=”display-4”>{title}</h1>
<p class=”lead”>{intro}</p>
<hr class=”my-4”>
<p v-if=”validated” :class=”errorTextClass”>Enter a valid search term</p>

<button
type=”button”
class=”btn btn-primary btn-lg mb-3”
v-on:click=”refreshPage”
v-if=”result.length > 1”
\>
<font-awesome-icon icon=”redo”/> Start again
</button>
<input
class=”form-control form-control-lg mb-3”
type=”search”
placeholder=”Search”
aria-label=”Search”
v-model=”search”
required
autocomplete=”off”
id=”search”
\>

<div v-for=”(result, index) in result” :key=”index”>
<div class=”media mb-4”>
<img
:src=”resizeArtworkUrl(result)”
alt=”Album Cover”
class=”album-cover align-self-start mr-3”
\>
<div class=”media-body”>
<h4 class=”mt-0”>
<!– <button
type=”button”
class=”btn btn-primary btn-lg mb-3 float-right”
v-on:click=”addItem(result)”
\>
<font-awesome-icon icon=”plus”/>
</button>–>

<button
type=”button”
class=”btn btn-primary btn-lg mb-3 float-right”
v-on:click=”addItem(result)”
\>
<font-awesome-icon icon=”plus”/>
</button>
<b>{result.collectionName}</b>
</h4>
<h6 class=”mt-0”>{result.artistName}</h6>
<p class=”mt-0”>{result.primaryGenreName}</p>
</div>
</div>
</div>

<div :class=”loadingClass” v-if=”loading”></div>

<button
class=”btn btn-success btn-lg btn-block mb-3”
type=”submit”
v-on:click=”getData”
v-if=”result.length < 1”
\>
<font-awesome-icon icon=”search”/> Search
</button>
</div>
</div>
</template>

<script>
import List from “../components/myList.vue”;

export default {
name: “Hero”,
components: {
List
},
data: function() {
return {
title: “Simple Search”,
isActive: true,
intro: “This is a simple hero unit, a simple jumbotron-style.”,
subintro:
“It uses utility classes for typography and spacing to space content out.”,
result: [],
errors: [],
List: [],
search: “”,
loading: “”,
message: false,
isValidationAllowed: false,
loadingClass: “loading”,
errorTextClass: “error-text”
};
},

watch: {
search: function(val) {
if (!val) {
this.result = [];
}
}
},

computed: {
validated() {
return this.isValidationAllowed && !this.search;
},
isDisabled: function() {
return !this.terms;
}
},

methods: {
getData: function() {
this.isValidationAllowed = true;
this.loading = true;
fetch(`https://restit.api.com\`)
.then(response => response.json())
.then(data => {
this.result = data.results;
this.loading = false;
/* eslint-disable no-console */
console.log(data);
/* eslint-disable no-console */
});
},

toggleClass: function() {
// Check value
if (this.isActive) {
this.isActive = false;
} else {
this.isActive = true;
}
},

refreshPage: function() {
this.search = “”;
},
addItem: function(result) {
this.List.push(result);
/* eslint-disable no-console */
console.log(result);
/* eslint-disable no-console */
},

resizeArtworkUrl(result) {
return result.artworkUrl100.replace(“100x100”, “160x160”);
}
}
};
</script>

<style>
.loading {
background-image: url(“../assets/Rolling-1s-42px.gif”);
background-repeat: no-repeat;
height: 50px;
width: 50px;
margin: 15px;
margin-left: auto;
margin-right: auto;
}

.error-text {
color: red;
}

.media {
text-align: left;
}

.album-cover {
width: 80px;
height: auto;
}

.red {
background: red;
}

.blue {
background: blue;
}

.div {
width: 100px;
height: 100px;
display: inline-block;
border: 1px solid black;
}
</style>

It is the addItem button which code is:

<button
type=”button”
class=”btn btn-primary btn-lg mb-3 float-right”
v-on:click=”addItem(result)”
\>
<font-awesome-icon icon=”plus”/>
</button>

List component

<template>
<div class=”mb-5 container”>
<button type=”button” class=”btn btn-primary mt-2 mb-2 btn-block”>
My List
<span class=”badge badge-light”>{List.length}</span>
</button>
<ul class=”list-group” v-for=”(result, index) in List” :key=”index”>
<li class=”list-group-item”>
<b>{result.collectionName}</b>
<h6 class=”mt-0”>{result.artistName}</h6>
<p class=”mt-0”>{result.primaryGenreName}</p>
</li>
</ul>
<ul>
</ul>
</div>
</template>

<script>
export default {
props: [
‘List’,
],

};
</script>

So the idea is users can add an item to a list component but obviously they can’t keep adding that item, so that is why I need to disable the button after clicked.

Solution :

Try with:

<button
type=”button”
class=”btn btn-primary btn-lg mb-3 float-right”
v-on:click=”addItem(result)”
:disabled=”result.disableButton”>
<font-awesome-icon icon=”plus”/>
</button>

And in the method:

addItem: function(result) {
result.disableButton = true; // Or result[‘disableButton’] = true;
this.List.push(result);
},

[Vue.js] Is it possible to dynamically add chart type in the extends property, based on props from parent component? Subscribe to RSS

there is a vue.js chartjs component which imports the whole vue-chartjs library. My idea is, is it possible to somehow pass the type of the chart which and add it to the ‘extends: VueCharts.charttype?.’ In the example I provide it extends the VueCharts.Line, I need this property to be dynamically interpolated, passed from props. Is it possible this charttype to come from a parent props dynamically and how?

<script>
import { VueCharts } from “vue-chartjs”;

export default {
extends: VueCharts.Line,
props: [“chartdata”, “options”],
mounted() {
this.renderChart(this.chartdata, this.options);
}
}
</script>
<style scoped>

</style>

Solution :

since extends the same as mixins, you need to pass a dynamic mixin, in order to do that you need two components, imagine we have component ChartWrapper :

<template>
<div>
<div>{ chartType }</div>
<chart :chart-data=”datacollection”/>
</div>
</template>

<script>
import Chart from “./Chart”;
import { VueCharts, mixins } from “vue-chartjs”;
const { reactiveProp } = mixins;
export default {
name: “ChartWrapper”,
components: {
Chart
},
props: {
chartType: {
type: String,
required: true
}
},
data() {
return {
datacollection: {
labels: [this.getRandomInt(), this.getRandomInt()],
datasets: [
{
label: “Data One”,
backgroundColor: “#f87979”,
data: [this.getRandomInt(), this.getRandomInt()]
},
{
label: “Data One”,
backgroundColor: “#f87979”,
data: [this.getRandomInt(), this.getRandomInt()]
}
]
}
};
},
methods: {
getRandomInt() {
return Math.floor(Math.random() * (50 - 5 + 1)) + 5;
}
},
created() {
if (this.chartType) {
Chart.mixins = [reactiveProp,VueCharts[this.chartType]];
}
}
};
</script>

this component takes chartType as a prop, and I import all charts as VueCharts in top of the script ==> 1

second component:

<script>
export default {
props: [“options”],
mounted() {
// this.chartData is created in the mixin.
// If you want to pass options please create a local options object
this.renderChart(this.chartData, this.options);
}
};
</script>

the second component just has options props, and renderChart function invoked.
\==> 2

What is happening?

the ChartWrapper component receives the chart type by chartType prop, in the created hook, if chartType exist, assign the chart(resolved by VueCharts[this.chartType]) to Chart component as a mixin in addition to reactiveProp,
I also pass the chart data to Chart component.

in the end, call the ChartWrapper component:

<ChartWrapper chartType=”Bar”/>

Live example on code sandbox: https://codesandbox.io/s/vue-template-w9r8k

[Vue.js] How to serve image files to frontend with Spring Data REST? Subscribe to RSS

I’m trying to create a movie database web app. Each movie should have a poster image. I don’t know how to correctly serve images to the frontend with Spring Data REST.

Movie.java

import lombok.AccessLevel;
import lombok.Data;
import lombok.NoArgsConstructor;

import javax.persistence.*;
import java.io.File;
import java.sql.Date;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

@Data
@Entity
@NoArgsConstructor(access = AccessLevel.PRIVATE)
public class Movie {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
private String title;
private String director;
private Date releaseDate;
private File posterFile;

@ManyToMany
@JoinTable(
name = “MOVIE_GENRES”,
joinColumns = @JoinColumn(name = “MOVIE_ID”),
inverseJoinColumns = @JoinColumn(name = “GENRE_ID”))
private Set<Genre> genres = new HashSet<>();

@OneToMany
@MapKeyColumn(name = “ACTOR_ROLE”)
private Map<String, Actor> cast = new HashMap<>();

public Movie(String title) {
this.title = title;
}

public void addActor(String role, Actor actor) {
cast.put(role, actor);
}

public void removeActor(String role) {
cast.remove(role);
}

public void addGenre(Genre genre) {
genres.add(genre);
}

public void removeGenre(Genre genre) {
genres.remove(genre);
}
}

I can’t use a byte-array in the movie bean because it is too large to be saved in the database. I could store the File object or a Path object or a String containing the path instead:
private File posterFile;
The problem is, that it will save a local path like “C:\user\documents\project\backend\images\posterxyz.png”.
When i try to use this path as img-src in my frontend it get error “Not allowed to load local resource”. I mean it sounds like a stupid way of doing this anyway. I just don’t know what the correct way to do this is.

This is the Movie Repository.
I’m using Spring Data REST in the backend that generates JSON in Hypermedia Application Language format.

MovieRepository.java

import org.springframework.data.repository.PagingAndSortingRepository;
import org.springframework.data.rest.core.annotation.RepositoryRestResource;

@RepositoryRestResource(collectionResourceRel = “movies”, path = “movies”)
public interface MovieRepository extends PagingAndSortingRepository<Movie, Long> {

}

Solution :

I would:

One

prevent the posterFile attribute from being serialized by adding the @JsonIgnore annotation to the field.

@JsonIgnore
private File posterFile;

You can also do this via a Jackson mix-in class to avoid ‘polluting’ you entities with Json processing instructions but you’ll need to research that yourself.

Two

Add a custom link to the resource representation that will allow clients to fetch the image data on demand. e.g. /movies/21/poster

See here for details on how you can add custom links to a resource:

Spring Data Rest Custom Links on Resource

Three

Create a standard Spring MVC controller bound to the path to which the custom link points and which will read the file data and stream the response.

e.g.

@Controller
public MoviePosterController{

@GetMapping(path=”/movies/{movieId}/poster”)
//https://docs.spring.io/spring-data/jpa/docs/current/reference/html/#core.web for auto resolution of path var to domain Object
public @ResponseBody byte[] getPoster(@PathVariable(“movieId”) Movie movie, HttpServletResponse response){
File file = movie.getPosterFile();
//stream the bytes of the file
// see https://www.baeldung.com/spring-controller-return-image-file
// see https://www.baeldung.com/spring-mvc-image-media-data
}
}

Solution 2:

This is not really possible with Spring Data/REST as it focusses on structured data; i.e. tables and associations for the most part. Yes, there are a few hoops you could jump through as explained in other answers but there is also a related project called Spring Content that addresses exactly this problem domain.

Spring Content provides the same programming paradigms as Spring Data/REST, just for unstructured data; i.e. images, documents, movies, etc. So, using this project you can associate one or more “content” objects with Spring Data entities and manage them over HTTP just like with the Spring Data Entities too.

Its pretty simple to add to the project, as follows:

pom.xml (boot starters also available)

<!– Java API –>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-jpa</artifactId>
<version>0.9.0</version>
</dependency>
<!– REST API –>
<dependency>
<groupId>com.github.paulcwarren</groupId>
<artifactId>spring-content-rest</artifactId>
<version>0.9.0</version>
</dependency>

Configuration

@Configuration
@EnableJpaStores
@Import(“org.springframework.content.rest.config.RestConfiguration.class”)
public class ContentConfig {

// schema management (assuming mysql)
//
@Value(“/org/springframework/content/jpa/schema-drop-mysql.sql”)
private Resource dropContentTables;

@Value(“/org/springframework/content/jpa/schema-mysql.sql”)
private Resource createContentTables;

@Bean
DataSourceInitializer datasourceInitializer() {
ResourceDatabasePopulator databasePopulator =
new ResourceDatabasePopulator();

databasePopulator.addScript(dropContentTables);
databasePopulator.addScript(createContentTables);
databasePopulator.setIgnoreFailedDrops(true);

DataSourceInitializer initializer = new DataSourceInitializer();
initializer.setDataSource(dataSource());
initializer.setDatabasePopulator(databasePopulator);

return initializer;
}
}

To associate content, add Spring Content annotations to the Movie entity.

Movie.java

@Entity
public class Movie {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
.. existing fields…
// private File posterFile; no longer required

@ContentId
private String contentId;

@ContentLength
private long contentLength = 0L;

// if you have rest endpoints
@MimeType
private String mimeType = “text/plain”;
}

Create a “store”:

MoviePosterContentStore.java

@StoreRestResource(path=”moviePosters”)
public interface MoviePosterContentStore extends ContentStore<Movie, String> {
}

This is all you need to create REST endpoints @ /moviePosters. When the application starts, Spring Content will look at the dependencies seeing Spring Content JPA, look at the MoviePosterContentStore interface and inject an implementation of that interface for JPA. It will also see the Spring Content REST dependency and inject an @Controller implementation that forwards HTTP requests to the MoviePosterContentStore. This saves you having to implement any of this yourself which I think is what you are after.

So…

To manage content with the injected REST API:

curl -X POST /moviePosters/{movieId} -F file=@/path/to/poster.jpg

will store the image in the database (as a BLOB) and associate it with the movie entity whose id is movieId.

curl /moviePosters/{movieId}

will fetch it again and so on…supports all CRUD methods and video streaming as well BTW!

There are a couple of getting started guides here. The reference guide for JPA is here. And there is a tutorial video here. The coding bit starts about 1/2 way through.

A couple of additional points:
- if you use the Spring Boot Starters then you don’t need the @Configuration for the most part.
- Just like Spring Data is an abstraction, so is Spring Content so you aren’t limited to storing the poster images as BLOBs in the database. You could store them on the file system or in cloud storage like S3 or any other storage supported by Spring Content.

HTH

[Vue.js] Nuxt application returns 404 when dynamic routes are refreshed (Tomcat Server) Subscribe to RSS

I’m doing the following:
- Production Build: npm run generate
- I copy the dist folder into the Tomcat Webapps, and it works fine
- Whenever I refresh a dynamic route, it shows 404

URLs that work:

https://ip:port/entryPath/dashboard/user

URLs that don’t work:

https://ip:port/entryPath/dashboard/user/123
https://ip:port/entryPath/dashboard/user/123/settings
https://ip:port/entryPath/dashboard/user/123/privacy

to be able to share an URL such as:

https://ip:port/entryPath/dashboard/user/123/activity

So other users just clicking the URL should be directly able to access it. But it just ends up with a 404, no matter where I deploy.

Please Note:
My intention is to deploy the dist folder contents on Tomcat webapps folder.

Solution :

Dynamic routes are ignored by the generate command.

If you want Nuxt.js to generate routes with dynamic params, you need to set an array of dynamic routes.

To generate the dynamic user pages, add to the nuxt.config.js:

generate: {
// create an array of all routes for generating static pages
// careful, this is only used by `npm run generate`. These must match SPA mode routes
routes: function () {
return axios.get(
https://jsonplaceholder.typicode.com/users'
)
.then((response) => {
let users = response.data.map((user) => {
return {
route: ‘/users/‘ + user.id,
payload: user
}
});
return [‘/some-other-dynamic-route-or-array-of-routes/‘, …users]
});
}
}

Here’s a nice article about static sites with Nuxt with examples on which the above is based.

[Vue.js] It's equivalent event emit angular/vue in blazor? Subscribe to RSS

Is it equivalent of event emit like the underneath in blazor ? :
https://angular.io/api/core/EventEmitter (in angular)
https://vuejs.org/v2/guide/components-custom-events.html (in vue)

in new blazor syntax @ event name in < > of component (3.0 preview 6)

Solution :

Yes, it is…

In Blazor, events are defined and triggered by means of delegates, particularly the Action delegate, Action<T> delegate with a single parameter, two, three…16 parameters, Func<T>, Func<T, TResult> and more, and most importantly the new structure EventCallback, EventCallback<T>, etc. All these delegates encapsulate methods that have the number of the given parameters.

In Angular you use something like this to call back a method:

this.valueChange.emit(this.counter);

In Blazor you use something like this to call the method encapsulated in the delegate.

Suppose you define a parameter property in a child component to encapsulate a method passed as a Component parameter from a Parent Component (This may be a requirement if you wish to call a method defined on the Parent component, and pass it a value, say an int type. The parameter property may be defined like this:

protected EventCallback<int> ValueChange {get; set;}

This is how to assign the delegate to the parameter property in the Parent Component

<ParentComponent>
<ChildComponent @onclick=”@CallParentMethod” ></ChildComponent>
</ParentComponent>

In the Child Component you should define a method named CallParentMethod from withing which it call the method encapsulated in the delegate property thus:

Task CallParentMethod()
{
ValueChange?.InvokeAsync(1);
}

As you can see, InvokeAsync is more or less equivalent to EventEmitter in Angular.

Note that I could call the Parent’ method’s directly using lambada expression.

Hope this helps…

[Vue.js] How to deploy Node.js Express server + Vue.js app on AWS EC2 Subscribe to RSS

I’m setting up my website which would run on an AWS Ubuntu EC2. It is a Vue.js SPA relying on a Nodejs Express app with API calls and socket.io. So far both apps are working, the backend is on my AWS EC2 free tier, behind an Elastic Load Balancer, the frontend is on my machine since I working on it. Now I would like to deploy the frontend to my AWS EC2 also but I’m confused how to do it correctly. The tutorials I’ve found are using nginx but I’m not sure that I need nginx as I already have AWS ELB. Any advices would be great :)

Solution :

as is says “If you are developing the frontend app separately from the backend - i.e. the backend exposes an API for the frontend to talk to, then the frontend is essentially a purely static app” here

I would choose s3 to host vue.js app because it’s static and can be served using s3 and
I will choose EC2 for hosting my API (server code) and also i’d make an elastic IP to talk to my ec2 server so that on restart i don’t have to handle the dynamic IP’s

Steps to make the website live

First pull yout node express server on the ec2 instance
start the node express server use pm2 to serve it as an service
expose the served port from security groups of the ec2 instance
make an s3 bucket on aws and upload files to it
Tip: just click upload button after dropping the files to s3 do not go clicking next
after uploading select all the uploaded files and then mark as public
after uploading go to properties of that bucket and then choose static web hosting and type index.html the asked field

** TIP: do not use a load balancer for this application use only when you distribute the system across multiple ec2’s**

[Vue.js] Why axios request not working inside vuejs (nuxt.js) methods Subscribe to RSS

there is installed axios inside my nuxt.js application. Here my configuration file code:

File: nuxt.config.js

modules: [
‘@nuxtjs/vuetify’,
‘@nuxtjs/axios’,
],

axios: {
// proxyHeaders: false
}

Here my example working code:

export default {
data() {
return {
ip: ‘’
}
},
async asyncData({ $axios }) {
const ip = await $axios.$get(‘http://icanhazip.com')
return { ip }
}
}

And here my not working code:

export default {
data() {
return {
ip: ‘’
}
},
methods: {
async asyncData() {
const ip = await this.$axios.$get(‘http://icanhazip.com')
this.ip = ip
}
}
}

Why inside methods axios request not working?

Solution :

You cannot call asyncData in you methods object. asyncData is for pre rendering only.

Rename the function to something else and it should be fine:

export default {
data() {
return {
ip: ‘’
}
},
methods: {
async getData() {
const ip = await this.$axios.$get(‘http://icanhazip.com')
this.ip = ip
}
}
}

Also when you are using asyncData as in the top example, you should not initialise “ip” in the data function. What is returned from asyncData is merged into data anyway.

[Vue.js] Apollo client Graphql query variable type error Subscribe to RSS

when trying to execute delete query in which IDis required but when getting an error.
“Variable “$id” of required type “ID!” was not provided”

Query

export const DELETE_CUSTOMER = gql`
mutation deleteCustomer($id:ID!){
deleteCustomer(
_id: $id
)
}
`

Mutation code in vuex actions

deleteCustomer(vuexContext,id){
return apollo
.mutate({
mutation: DELETE_CUSTOMER,
variables: id.toString()
})
.then(()=>{
vuexContext.commit(‘deleteCustomer’,id.toString());
})
.catch((err) => {
throw err;
});

}

Solution :

The variables option should be an Object, with each property mapping to an individual variable referenced inside the query. You cannot assign the value of an individual variable to variables like you’re doing. The corrected method call would look something like:

apollo.mutate({
mutation: DELETE_CUSTOMER,
variables: { id: id.toString() },
})

[Vue.js] How to dynamically set the immediate property in watch Subscribe to RSS

These components may be used in different places, some need the immediate attribute in the watch, some do not need

Boolean values related to variables are invalid

watch:{
color:{
handler () {
let rgba = this.color.rgba;
let currentColor = rgba.a === 1 ? this.color.hex : `rgba(${rgba.r},${rgba.g},${rgba.b},${rgba.a})`;
this.submit(‘color’,this.now,currentColor)
},
immediate: !(this.now===”standard”)

}
}

Solution :

I would try set the watch property on the vue.js instance once you have the value you want then wrote something like this:

//get info for my immediate value then instantiate a watcher like this:
this.$watch(‘$data.color’, this.handler, { deep: true })
or
this….{deep: false}

[Vue.js] Deep copy in ES6 using the spread syntax Subscribe to RSS

when trying to create a deep copy map method for my Redux project that will work with objects rather than arrays. I read that in Redux each state should not change anything in the previous states.

export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {

output[key] = callback.call(this, {…object[key]});

return output;
}, {});
}

It works:

return mapCopy(state, e => {

if (e.id === action.id) {
e.title = ‘new item’;
}

return e;
})

However it does not deep copy inner items so I need to tweak it to:

export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {

let newObject = {…object[key]};
newObject.style = {…newObject.style};
newObject.data = {…newObject.data};

output[key] = callback.call(this, newObject);

return output;
}, {});
}

This is less elegant as it requires to know which objects are passed.
Is there a way in ES6 to use the spread syntax to deep copy an object?

Solution :

No such functionality is built-in to ES6. I think you have a couple of options depending on what you want to do.

If you really want to deep copy:

Use a library. For example, lodash has a cloneDeep method.
Implement the own cloning function.

Alternative Solution To the Specific Problem (No Deep Copy)

However, I think, if you’re willing to change a couple things, you can save yourself some work. I’m assuming you control all call sites to the function.

Specify that all callbacks passed to mapCopy must return new objects instead of mutating the existing object. For example:

mapCopy(state, e => {
if (e.id === action.id) {
return Object.assign({}, e, {
title: ‘new item’
});
} else {
return e;
}
});

This makes use of Object.assign to create a new object, sets properties of e on that new object, then sets a new title on that new object. This means you never mutate existing objects and only create new ones when necessary.
mapCopy can be really simple now:

export const mapCopy = (object, callback) => {
return Object.keys(object).reduce(function (output, key) {
output[key] = callback.call(this, object[key]);
return output;
}, {});
}

Essentially, mapCopy is trusting its callers to do the right thing. This is why I said this assumes you control all call sites.

Solution 2:

Instead use this for deep copy

var newObject = JSON.parse(JSON.stringify(oldObject))

var oldObject = {
name: ‘A’,
address: {
street: ‘Station Road’,
city: ‘Pune’
}
}
var newObject = JSON.parse(JSON.stringify(oldObject));

newObject.address.city = ‘Delhi’;
console.log(‘newObject’);
console.log(newObject);
console.log(‘oldObject’);
console.log(oldObject);

Solution 3:

From MDN

Note: Spread syntax effectively goes one level deep while copying an array. Therefore, it may be unsuitable for copying multidimensional arrays as the following example shows (it’s the same with Object.assign() and spread syntax).

Personally, I suggest using Lodash’s cloneDeep function for multi-level object/array cloning.

Here is a working example:

const arr1 = [{ ‘a’: 1 }];

const arr2 = […arr1];

const arr3 = _.clone(arr1);

const arr4 = arr1.slice();

const arr5 = _.cloneDeep(arr1);

const arr6 = […{…arr1}]; // a bit ugly syntax but it is working!

// first level
console.log(arr1 === arr2); // false
console.log(arr1 === arr3); // false
console.log(arr1 === arr4); // false
console.log(arr1 === arr5); // false
console.log(arr1 === arr6); // false

// second level
console.log(arr1[0] === arr2[0]); // true
console.log(arr1[0] === arr3[0]); // true
console.log(arr1[0] === arr4[0]); // true
console.log(arr1[0] === arr5[0]); // false
console.log(arr1[0] === arr6[0]); // false
<script src=”https://cdnjs.cloudflare.com/ajax/libs/lodash.js/4.17.4/lodash.js"></script>

Solution 4:

I often use this:

function deepCopy(obj) {
if(typeof obj !== ‘object’ || obj === null) {
return obj;
}

if(obj instanceof Date) {
return new Date(obj.getTime());
}

if(obj instanceof Array) {
return obj.reduce((arr, item, i) => {
arr[i] = deepCopy(item);
return arr;
}, []);
}

if(obj instanceof Object) {
return Object.keys(obj).reduce((newObj, key) => {
newObj[key] = deepCopy(obj[key]);
return newObj;
}, {})
}
}

Solution 5:

function deepclone(obj) {
let newObj = {};

if (typeof obj === ‘object’) {
for (let key in obj) {
let property = obj[key],
type = typeof property;
switch (type) {
case ‘object’:
if( Object.prototype.toString.call( property ) === ‘[object Array]‘ ) {
newObj[key] = [];
for (let item of property) {
newObj[key].push(this.deepclone(item))
}
} else {
newObj[key] = deepclone(property);
}
break;
default:
newObj[key] = property;
break;

}
}
return newObj
} else {
return obj;
}
}

Solution 6:

// use: clone( <thing to copy> ) returns <new copy>
// untested use at own risk
function clone(o, m){
// return non object values
if(‘object’ !==typeof o) return o
// m: a map of old refs to new object refs to stop recursion
if(‘object’ !==typeof m || null ===m) m =new WeakMap()
var n =m.get(o)
if(‘undefined’ !==typeof n) return n
// shallow/leaf clone object
var c =Object.getPrototypeOf(o).constructor
// TODO: specialize copies for expected built in types i.e. Date etc
switch(c) {
// shouldn’t be copied, keep reference
case Boolean:
case Error:
case Function:
case Number:
case Promise:
case String:
case Symbol:
case WeakMap:
case WeakSet:
n =o
break;
// array like/collection objects
case Array:
m.set(o, n =o.slice(0))
// recursive copy for child objects
n.forEach(function(v,i){
if(‘object’ ===typeof v) n[i] =clone(v, m)
});
break;
case ArrayBuffer:
m.set(o, n =o.slice(0))
break;
case DataView:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.byteLength))
break;
case Map:
case Set:
m.set(o, n =new (c)(clone(Array.from(o.entries()), m)))
break;
case Int8Array:
case Uint8Array:
case Uint8ClampedArray:
case Int16Array:
case Uint16Array:
case Int32Array:
case Uint32Array:
case Float32Array:
case Float64Array:
m.set(o, n =new (c)(clone(o.buffer, m), o.byteOffset, o.length))
break;
// use built in copy constructor
case Date:
case RegExp:
m.set(o, n =new (c)(o))
break;
// fallback generic object copy
default:
m.set(o, n =Object.assign(new (c)(), o))
// recursive copy for child objects
for(c in n) if(‘object’ ===typeof n[c]) n[c] =clone(n[c], m)
}
return n
}