Reference (not copy) a class as a member of another Class - Mootools - javascript

I have the following classes
The Box Class
var Box = new Class({
Implements: [Options],
options: {
name: 'new',
weight: 0
},
initialize: function (options) {
this.setOptions(options);
},
getParent: function () {
return this.options.parent;
}
});
The Collection Class
var Collection = new Class({
Implements: [Options],
options: {
boxes: []
},
boxes: [],
initialize: function (options) {
var self = this;
this.setOptions(options);
Array.each(this.options.boxes, function (box) {
self.boxes.push(new Box({
parent: self,
name: box.name,
weight: box.weight
}));
});
}
});
I am passing the Collection class (as the parent) to the Box Class when it is created.
var newCollection = new Collection({
boxes: [
{
name: 'A',
weight: 9
},
{
name: 'B',
weight: 3
},
{
name: 'C',
weight: 2
},
{
name: 'D',
weight: 5
},
{
name: 'E',
weight: 7
}
]
});
I want the parent in the Box class to be a reference to the Collection class and not a copy, although It seems that I get a copy of the newCollection class each time the Box class is created (the length of the boxes is different for each one)
Array.each(newCollection.boxes, function (box) {
console.log('*',box.getParent());
});
I am new to mootools and even though I have gone through the documentation, this is the way I ended up writing code. Is there a more accepted coding pattern in mootools by which I can reference the parent?
Here's the fiddle.

Easy to miss. setOptions (docs) deep copies the options object. Just set the parent property of your Box after initializing it. I've posted a slightly modified version of your code here
Array.each(this.options.boxes, function (box) {
var nB = new Box({
name: box.name,
weight: box.weight
});
// Note the difference
nB.parent = self;
self.boxes.push(nB);
});

Related

aurelia search filtering using a computed is causing issues when rendering to view

I have a search function that retrieves objects
this.searchForObjectByName = () => {
this.server.get('objects/?searchTerm=' + self.searchTerm)
.then(groupObjects=>
{
this.searchResults = groupObjects;
});
}
I then set the result of this to a property called searchResults, which are a groups of objects.
I now want to perform some filtering base on the results. Say I have some groups of objects that look like:
{
id: 1,
name: 'group 1',
objects: [
{
id: 1,
name: 'object name 1',
categories: ['Cat A', 'Cat B']
},
{
id: 2,
name: 'object name 2',
categories: ['Cat A', 'Cat D']
},
{
id: 3,
name: 'object name 3',
categories: ['Cat C', 'Cat D']
}
]
},
{
id: 2,
name: 'group 2',
objects: [
{
id: 4,
name: 'object name 4',
categories: ['Cat A', 'Cat F']
},
{
id: 5,
name: 'object name 5',
categories: ['Cat C', 'Cat D']
}
]
}
I would like to then filter to only show Cat A objects.
I have a computed that filters the search results based on selected categories:
get filteredSearchResults(): any[] {
var filteredGroups = this.searchResults.map(res => {
let filteredGroup = new Group;
filteredGroup = res.id,
filteredGroup = res.name;
filteredGroup = res.objects.filter(o => this.filteredCategories.some(fc => o.categories.some(oc => oc == fc)));
return filteredGroup;
});
filteredGroups = filteredGroups.filter(fg => fg.objects.length > 0);
return filteredGroups;
}
This filters the results fine and I can select different categories and it filters correctly. However it jumps all over the place in the UI, the results are constantly updating due to using a computed to filter the results. I can't use the computedfrom attribute because its based on the filtered categories I've selected (that are an array) and the results returning from the server (also an array). Has anyone else experienced this issue before? Everything I try causes jitter and I'm not sure how I can do this differently.
I could trigger events when selected categories change or when data is returned from the server, I could then recalculate the filtered results. This would work but is hacky and would prefer to be able to do this in a computed
Without computedFrom, your property getter is being polled, which is why your interface is jumping around. I'm not familiar with .some, but I'm guessing order is not guaranteed. While you could simply order your search results to work around this, it would be pretty inefficient to be filtering and sorting an array every 5 seconds.
Using a collection observer your filter code will only be executed when your interface filters actually change. I haven't actually tested this code
import { BindingEngine, Disposable, bindable } from 'aurelia-framework';
export class MyPage
{
#bindable searchTerm: string;
filteredSearchResults: any[];
subscription: Disposable;
constructor(private bindingEngine: BindingEngine)
{
}
attached()
{
this.subscription = this.bindingEngine.collectionObserver(this.filteredCategories).subscribe(this.filtersChanged);
}
detached()
{
this.subscription.dispose();
}
searchTermChanged(newValue: string, oldValue: string)
{
this.find();
}
find()
{
this.server.get('objects/?searchTerm=' + this.searchTerm)
.then(groupObjects=>
{
this.searchResults = groupObjects;
this.update();
});
}
update()
{
// your original filtering logic unchanged
var filteredGroups = this.searchResults.map(res =>
{
let filteredGroup = new Group;
filteredGroup = res.id,
filteredGroup = res.name;
filteredGroup = res.objects.filter(o => this.filteredCategories.some(fc => o.categories.some(oc => oc == fc)));
return filteredGroup;
});
filteredGroups = filteredGroups.filter(fg => fg.objects.length > 0);
// set value instead of using a computed property
this.filteredSearchResults = filteredGroups;
}
filtersChanged(splices)
{
this.update(); // you could remove this method and bind the array observable directly to update()
}
}

VueJs v-for loop not updating correctly

Im trying to make a form where you have buttons to add children inputs but after adding one child the button no longer works and it returns to error in the cole as to why but if i output the data it looks like it is being adding just the DOM isn't being updated with the new input box
var default_item = {
value: 0,
reps: 0,
sets: 0
}
var deafult_exercise = {
exercise_name: '',
item_data: [default_item]
};
new Vue({
el: '#app',
data: {
log_data: [
{
log_name: 'log #1',
log_day: 1,
exercise_data: [
{
exercise_name: 'exercise #1',
item_data: [
{
value: 50,
reps: 5,
sets: 5
}
]
}
]
}
]
},
methods: {
addLog: function(){
this.log_data.push({log_name: '', log_day:0, exercise_data: deafult_exercise});
},
addExercise: function(index_id) {
this.log_data[index_id].exercise_data.push(deafult_exercise);
},
addItem: function(index_id, log_id) {
this.log_data[log_id].exercise_data[index_id].item_data.push(default_item);
}
}
})
JSfiddle: https://jsfiddle.net/renlok/5mpace1q/3/
Your issue is that you are pushing the same items and referencing the same items so essentially you are only ever adding 1 item
You need to use
Object.assign({}, default_item)
To use new instances of the items see updated fiddle https://jsfiddle.net/5mpace1q/4/

How to add new attribute in data object?

For arrays vue.js good works with default array methods: push, pop, shift, reverse... But how to create new item in object and paste some value?
new Vue({
el: '#demo',
data: {
items: [
{
start: '12:15',
end: '13:15',
name: 'Whatch Vue.js Laracast',
description: 'Watched the Laracast series on Vue.js',
tags: ['learning', 'Vue.js', 'Laracast', 'PHP'],
note: "Vue.js is really awesome. Thanks Evan You!!!"
},
{
start: '13:15',
end: '13:30',
name: "Rubik's Cube",
description: "Play with my Rubik's Cube",
tags: ['Logic', 'Puzzle', "Rubik's Cube"],
note: "Learned a new algorithm."
}
],
},
methods: {
addItemAttribute: function(name, text) {
this.items[0][name] = text;
}
}
});
You can add properties to a Vue object similar to a normal javascript object. Reference: https://vuejs.org/api/#Options-Data
var obj = { a: 1};
obj.a // 1
obj.b // undefined
obj.b = 2;
// Now obj.b is defined
obj.b // 2
You can access data from Vue object like this
var vm = new Vue({
data: {
a: 1,
b: 2
}
});
Now we can access data from $data property
vm.$data.a === 1;
// You can add a new property
vm.$data.c = 3;
// it can be any object
vm.$data.d = {id: 1, name: 'ABC'}
You get and update props of a component using component.options.props
var component = Vue.component('props-demo-advanced', {
props: {
// just type check
size: Number,
// type check plus other validations
name: {
type: String,
required: true,
// warn if not two way bound
twoWay: true
}
}
});
Let's say, we want to make name twoWay binding to false
component.options.props.name.twoWay = faslse
Or even you can change the entire object.
component.options.props = {
size: Number,
type: {
type: Number,
required: false,
twoWay: false
}
}
I don't know the above configurations are correct or not, I am just trying to convey the idea.
You should learn Javascript first, how objects are made, how to change them etc.

Proper way of updating js-data relation objects

I have a linked list of objects of the same type:
GET /cards.json
[{
"id": 1,
"lesserCardId": null,
"greaterCardId": 2,
}, {
"id": 2,
"lesserCardId": 1,
"greaterCardId": 3
}, {
"id": 3,
"lesserCardId": 2,
"greaterCardId": null
}]
Resource definition:
DS.defineResource({
name: 'card',
relations: {
belongsTo: {
card: [{
localField: 'lesserCard',
localKey: 'lesserCardId'
}, {
localField: 'greaterCard',
localKey: 'greaterCardId'
}]
}
}
});
js-data correctly reads json and creates object hierarchy. What I want next is to automatically update linked card properties like this:
var card1 = DS.get('card', 1);
var card4 = DS.inject('card', {'id': 4});
card1.greaterCard = card4;
// corresponding liked object properties should be updated automatically
expect(card4.lesserCard).toBe(card1);
expect(card2.lesserCard).toBeUndefined();
I have achieved this without js-data making custom setter like this:
Object.defineProperty(Card.prototype, 'greaterCard', {
set: function (card) {
// custom logic for updating linked list structure
if (card === this.$greaterCard) {
return;
}
this.$greaterCard.lesserCard = this.$lesserCard;
this.$greaterCard = card;
if (card) {
card.lesserCard = this;
}
// updating depending properties
this.$updateCardLevel();
this.$updateCardLevelMax();
},
get: function () {
return this.$greaterCard;
}
});
But js-data introduces it's own property descriptors for relation objects. So this approach cannot be applied. I haven't found a way to hook into js-data relation property setters. I could make a separate service with method like cardService.setGreaterCard(card, greaterCard); which would update list structure, but this wouldn't be so convenient. Is there better way for updating linked objects on property change?
js-data version: 2.9.0
In JSData 2.x you would do this:
var Card = DS.defineResource({
name: 'card',
relations: {
belongsTo: {
card: [
{
localField: 'lesserCard',
localKey: 'lesserCardId',
link: false
},
{
localField: 'greaterCard',
localKey: 'greaterCardId',
link: false
}
]
}
}
});
Object.defineProperties(Card[Card.class].prototype, {
lesserCard: {
set: function (lesserCard) {
this.lesserCardId = lesserCard.id;
lesserCard.greaterCardId = this.id;
},
get: function () {
return Card.get(this.lesserCardId);
}
},
greaterCard: {
set: function (greaterCard) {
this.greaterCardId = greaterCard.id;
greaterCard.lesserCardId = this.id;
},
get: function () {
return Card.get(this.greaterCardId);
}
}
});
In JSData 3.x you would just do this:
store.defineMapper('card', {
relations: {
belongsTo: {
card: [
{
localField: 'lesserCard',
foreignKey: 'lesserCardId',
set: function (Relation, card, lesserCard, originalSet) {
lesserCard.greaterCardId = card.id;
originalSet(lesserCard);
}
},
{
localField: 'greaterCard',
foreignKey: 'greaterCardId',
set: function (Relation, card, greaterCard, originalSet) {
greaterCard.lesserCardId = card.id;
originalSet(lesserCard);
}
}
]
}
}
});

VueJs How to duplicate object from v-repeat?

Functionality allows you to add/delete description, title and time for the event.
I can not deal with the duplication(cloning) of the object which is created through v-model = (event.name, event.description and event.date)
All works fine with the removing selected object, it works like this:
deleteEvent: function(index){
if(confirm('Are you sure?')) {
this.events.$remove(index);
}
}
Here's an example of my code for a application to adding and changing events.
var vm = new Vue({
el: '#events',
data:{
event: { name:'', description:'', date:'' },
events: []
},
ready: function(){
this.fetchEvents();
},
methods: {
fetchEvents: function() {
var events = [
{
id: 1,
name: 'TIFF',
description: 'Toronto International Film Festival',
date: '2015-09-10'
},
{
id: 2,
name: 'The Martian Premiere',
description: 'The Martian comes to theatres.',
date: '2015-10-02'
},
{
id: 3,
name: 'SXSW',
description: 'Music, film and interactive festival in Austin, TX.',
date: '2016-03-11'
}
];
this.$set('events', events);
},
addEvent: function() {
if(this.event.name) {
this.events.push(this.event);
this.event = { name: '', description: '', date: '' };
}
},
deleteEvent($index)"
deleteEvent: function(index){
if(confirm('Вы точно хотите удалить данную запись?')) {
this.events.$%remove(index);
}
},
cloneItem: function(index) {
}
}
});
there full code
http://codepen.io/Monocle/pen/ojLYGx
I found undocumented built it extend function Vue.util.extend that is equivalent to jQuery's extend.
In this case, you can avoid the enumerating the object properties
cloneItem: function(index) {
this.events.push(Vue.util.extend({},this.events[index]));
}
Access the cloned object via this.events[index], and then use it's properties to create new object and push it into events array:
cloneItem: function(index) {
this.events.push({ name: this.events[index].name,
description: this.events[index].description,
date: this.events[index].date });
}
Demo: http://codepen.io/anon/pen/MajKZO

Categories

Resources