VueJS - How to make models and collections? - collections

I am attempting to learn VueJS and I'm finding it hard to understand how to make models and collections.
For example, I want to have a collection (or list) of Employees and each Employee is a model.
But I'm not sure how to accomplish this in VueJS
Many thanks

Vue was initially created to bind data to a template in a reactive way, therefore, there's no "controller" or "model" notion like you would have in a regular MVC.
Vue just needs plain javascript objects as data, so if some of your data needs to be mapped to a model, well it's not about Vue, it's about... Javascript.
Here is an example of implementation (in ES6) :
class UserModel {
constructor(data) {
this.data = data
}
name() {
return this.data.firstname + ' ' + this.data.lastname
}
// and so on, put other methods here
}
var app = new Vue({
el: '#app',
data: {
user: {}
},
created() {
// axios or anything else for your ajax, or a new fetch api
axios.get('/me')
.then(response => {
// of course the "this" here is the Vue instance because i used an es6 arrow function
this.user = new UserModel(response.data)
})
}
})
That's it.
As you can see, Vue doesn't have to do with the Model class I created, I just mapped my ajax data to a custom class, then mapped the result to the Vue instance.
As simple as that.

Related

GraphQL.Net List of int as argument

I am trying to build an application using .Net and GraphQL. I need to get materials. not all of them but with the given Ids. When I pass it via playground or client side, I don't have any problem when I debug but I am not sure how to parse in the server side.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<IntGraphType>("ids");
// Do some action to bring datas
// Send data back
}
What am I missing here is there any methods to parse this in to list of int back?
Instead of using a GraphType for retrieving the argument, use the .NET type you want.
name: "materialsByIds",
arguments: new QueryArguments(
new QueryArgument<ListGraphType<IntGraphType>> { Name = "ids"}),
resolve: async (context) =>
{
var ids = context.GetArgument<List<int>>("ids");
// Do some action to bring datas
// Send data back
}
you can use MediatR. Create a Query class and pass it to mediateR. In CQRS, Command is used to write on DB(Create/Delete/Update of CRUD) and Query is used to read from DB(Read of CRUD).
create 'GetMaterialsByIdsQuery' and inside it write your code to get your materials by Id. then use it inside 'resolve' like this:
resolve: async context =>
{
var ids = context.GetArgument<List<int>>("ids");
return await mediator.Send(new GetMaterialsByIdsQuery(ids));
})
another way is that you can return somthing like MaterialsRepository.GetMaterialsByIds(ids) instead of using mediatR. But it is not recommended to use repository here. you can create a service class, inside it use your repository and then use your service here.

Binding VuexFire to a collection filtered with a query

I'm unsuccessfully trying to bind a Vuex state attribute to a queried collection in FireStore. I was wondering if anyone with more experience could point me in the right direction. This is what I'm currently doing:
In a Vuex Module called auth I'm declaring the following bind to userArticles
export const bindUserArticles = firestoreAction(({ bindFirestoreRef }, id) => {
return bindFirestoreRef('userArticles', userCollectionRef('articles', id))
})
This in turn points to a firebase method for querying the data (which works)
export const userCollectionRef = (collectionName, id) => {
return firestore().collection(collectionName).where("author.idAuthor", "==", id)
}
And I'm importing and dispatching the method in my Vue file in the following way
computed: {
...mapGetters('user', ['currentUser']),
},
methods: {
...mapActions('articles', ['bindUserArticles']),
},
watch: {
currentUser () {
this.bindUserArticles(this.currentUser.id)
}
}
So when the currentUser is updated upon login the method is triggered. The method is triggered and the right id is being sent, I've tested it with console.log. There is no error being displayed. When I try for example to modify the idAuthor of an existing article in the database, the list userArticles does not update. When I try adding or deleting an article from the database that has the specific idAuthor, the list userArticles does not update. I've also tried placing the this.bindUserArticles(this.currentUser.id) in the created() and mounted() life-cycle, to no avail.Does anyone have a clue where I'm going wrong about this?
Thanks in advance

Vuejs 2, VUEX, data-binding when editing data

I have a user profile section and Im trying to allow the user to edit their information. I am using vuex to store the user profile data and pulling it into the form. The edit form is located in a child component of the userProfile component - which loads the data save commits it to VUEX.
So I can populate the form with the data from VUEX, but as soon as I change any values in the form, it changes the value in my parent component as well.
I am not committing changes to VUEX until the form is saved, so it means the data is bound two way to VUEX. I was under the impression this was not possible. In this case it is not desired since if the user changes some data, then navigates away without actually clicking "save", the data is VUEX is still changed.
Note, this is a simplified example. Im actually using router view to load the child component, or I would pass the data through props. I have tested loading the edit-profile component directly like it is below, and I have the same issue.
Please see the code below, I can't find why the data is being sent back up to the store. Any help is greatly appreciated.
In the parent, I set retrieve the user data like so:
<template>
<div class="content">
<h1>{{getUserDetails.firstname}} {{getUserDetails.lastname}} </h1>
<edit-profile></edit-profile>
</div>
</template>
<script>
import { mapGetters } from 'vuex';
import EditProfile from './Edit.vue';
export default {
data() {
return {
// data
}
},
created () {
this.fetchData();
},
components: {
EditProfile:EditProfile
},
computed: mapGetters([
'getUserDetails'
]),
methods: {
fetchData: function () {
var _this = this;
// ajax call - then
_this.$store.commit('setData', {
name: 'userDetails',
data: response.data.data.userDetails
});
}
}
}
</script>
This loads the results and stores them in the store, and works great.
My store has this:
const store = new Vuex.Store({
state: {
userDetails: {}
},
mutations: {
setData(state, payload){
state[payload.name] = payload.data;
}
},
getters: {
getUserDetails: state => {
return state.userDetails;
}
}
}
Everything here is working.
In my child component with the edit form, I am populating the form like this:
<template>
<form>
<label>Name</label>
<input name="firstname" v-model="profile.firstname">
<input name="lastname" v-model="profile.lastname">
<button v-on:click="save">submit</button>
</form>
</template>
<script>
import {mapGetters } from 'vuex';
export default {
data() {
return {
profile:{}
}
},
watch: {
getUserDetails (newData){
this.profile = newData;
}
},
created (){
this.profile = this.$store.getters.getUserDetails;
},
computed: mapGetters([
'getUserDetails'
]),
methods:{
save (){
var _this = this;
// ajax call to POST this.profile then
_this.$store.commit('setData', {
name: 'userDetails',
data: this.profile
});
}
}
}
</script>
If you are looking for a non binding solution with vuex you can clone the object and use the local version for v-model than on submit commit it.
in your created lifecycle function do this:
created (){
this.profile = Object.assign({}, this.$store.getters.getUserDetails);
},
Why I think it is not working as expected for you: you're receiving an object, binding it to a local property. Then when you change that local property, it's bound by object pointer (/memory address) to the store's object.
Creating a new object and setting the properties on that new object based on the properties of the state's user profile object should do the trick, since the new object would have it's own address in memory, would point to another place...
Illustration:
created (){
// create a new object with {...}
this.profile = {
// assign values to properties of same name
firstName: this.$store.getters.getUserDetails.firstName,
lastName: this.$store.getters.getUserDetails.lastName,
};
},
However if those properties (firstName, lastName) are objects or arrays (anything accessed by pointer to memory address) then this wouldn't work either.
So... what I'd most likely end up doing myself in this situation is something like this:
data() {
return {
firstName: '',
lastName: ''
}
},
This defines local properties. When loading the data, you would populate the local values with profile data you have in the Vuex store.
created() {
let profile = this.$store.getters.getUserDetails;
if (profile.firstName && profile.lastName) {
this.firstName = profile.firstName;
this.lastName = profile.lastName;
}
},
Then, when saving, you use your local variables to update the store's values.
methods: {
save() {
let profile = {
firstName: this.firstName,
lastName: this.lastName
};
// ajax call to POST this.profile then
this.$store.commit('setData', {
name: 'userDetails',
data: profile
});
}
}
I'm writing this from the top of my head, so there might be a bug or typo in here... But I hope at the very least my logic is correct ;-P and clear to you.
Pro: until you're ready to save the edited information, you're not reflecting it anywhere else.
Con: if you'd need to reflect temporary changes (maybe in a User Profile Preview area), this might or might not work depending on your app's structure. You might want to bind or save on #input to a state.temporaryUserProfile object in that case?
I am still new to Vue.js, started using it 2 weeks ago. Hope this is clear and correct :)
The problem is caused by using v-model with mapGetters - this creates the two-way binding you've described. The simple solution is to use value instead:
:value="profile.firstName"
This way the form is only changing the local copy of field and not pushing the changes back to the Vuex store.
#AfikDeri solution is great, but it only create a shallow copy(for example it wont work if you have nested objects, which is common to have), to solve this you may serialize then parse your vuex state object getUserDetails, as follow:
created (){
this.profile = JSON.parse(JSON.stringify(this.$store.getters.getUserDetails));
}

How to use flux with meteor

I am starting on a new project and I want to keep my code as structured as possible. I was planning on using the flux pattern for the front-end but it feels like the event driven process that flux follows, goes against the grain of the reactionary way that meteor handles data and view updates.
Here is an example of what one of my stores might look like using flux and meteor
import { EventEmitter } from 'events'
import appDispatcher from '../Dispatcher'
import Meteor from 'meteor/meteor'
class TaskStore extends EventEmitter {
constructor() {
super();
this.tasks = [];
Meteor.subscribe('tasks');
}
addTask(task) {
Meteor.addTask(task)
this.tasks = Meteor.findAll();
}
getTasks() {
return this.tasks
}
}
const taskStore = new TaskStore();
appDispatcher.register((payload) => {
switch (payload.actionName) {
case 'CLICK':
taskStore.addTask(payload.newItem.action);
taskStore.emit('ADD_TASK');
break;
default:
}
});
export default taskStore
It's pretty straight forward, the store responds to the dispatcher by adding a task to the mongo database, then updates the local model with the data from the database and emits a change event. The view would respond by calling the getTasks() method and updating the state.
This works, but it doesn't feel very reactionary. I need a separate method exposed for finding all the tasks for example, where as in the documentation for react views that meteor provides, they have their own special function that wraps components and updates the props of the component whenever the data changes
export default createContainer(() => {
Meteor.subscribe('tasks');
return {
tasks: Tasks.find({}, { sort: { createdAt: -1 } }).fetch(),
incompleteCount: Tasks.find({ checked: { $ne: true } }).count()
})
This seems to be the way that meteor was designed. Views react to data changes and update in real time across all platforms, and I'm just not certain if my implementation of the flux pattern is the best way to stay true to that design, or if I should even bother trying to stay true to that design at all.
As a disclaimer, I'm still extremely new to both the flux pattern, and the Meteor framework.

Angular2 Async Data Issue

I have a component that is reliant upon an asynchronously fetched object. There are also child components in the template that rely on some data from this same object.
The problem I am having seems to be a classic race condition, but I'm not familiar enough with Angular 2 to understand the solution.
Take this for instance:
export class SampleComponent {
constructor(service: SomeService) {
this.service = service;
this._loadData();
}
private _loadData() {
this.service.getData().subscribe(data => this.data = data);
}
}
But in the template, I have child components to display certain parts of this.data:
<taglist tags="data?.tags"></taglist>
Now the Component for taglist looks something like:
#Component({
selector: 'taglist',
directives: [NgFor],
inputs: ['tags'],
template: `<span *ngFor="#tag of tags">{{ tag }}</span>`
})
export class TagList {
public tags: Array<string> = [];
constructor() {
//
}
}
Because tags input is received from an async loaded dataset, its not present when the Tag Component is initialized. What can I do so that when this.data load is finished, the sub components that use it will automatically access the newly loaded data?
Thank you for any insight you may be able to provide me!
Implement ngOnChanges() (https://angular.io/docs/ts/latest/api/core/OnChanges-interface.html)
#Component({
selector: 'taglist',
inputs: ['tags'],
template: `<span *ngFor="let tag of tags">{{ tag }}</span>`
})
export class TagList {
public tags: Array<string> = [];
constructor() {
//
}
ngOnChanges(changes: {[propName: string]: SimpleChange}) {
console.log('ngOnChanges - tags = ' + changes['tags'].currentValue);
}
}
For angular to handle the binding also use
<taglist [tags]="data?.tags"></taglist> or <taglist tags="{{data?.tags}}"></taglist>
otherwise it's a simple string to attribute assignment Angular doesn't handle.
This is the usual use case for the built-in async pipe, that is meant to be used to easily consume observables.
Try the following, first create a getter than returns directly the service layer observable:
get data(): Observable {
return this.service.getData();
}
Then you can consume this observable directly in the template using the async pipe:
<span *ngFor="let tag of data | async">{{ tag }}</span>

Resources