Ember pushObjects() not notifying computed property - javascript

I'm trying to use ember-light-table and I'm having some troubles on updating my array of objects.
When I use the action updateList(), I can see both arrays changing (adding/removing objects to the list), but the computed property tableModel is not triggered!
I thought pushObjects() would do the trick, but it's not notifying for some reason (it's adding). I also tried to initialize select_people with Ember.A(), although [] should already be an ember array...
My mixin:
// table-testing
import Ember from 'ember';
import Table from 'ember-light-table';
export default Ember.Mixin.create({
table: null,
tableColumns: null,
tableModel: null,
init() {
this._super(...arguments);
let table = new Table(this.get('tableColumns'), this.get('tableModel'), { enableSync: this.get('enableSync') });
this.set('table', table);
}
});
My controller
import Ember from 'ember';
import TableTesting from '../mixins/table-testing';
const { computed } = Ember;
export default Ember.Controller.extend(TableTesting, {
tableColumns: computed(function() {
return [{
label: 'First Name',
valuePath: 'firstName',
width: '50%',
sortable: false,
}, {
label: 'Last Name'
valuePath: 'lastName',
width: '50%'
}]
}),
tableModel: computed('selected_people.#each.firstName', function() {
// THIS MESSAGE ONLY SHOW WHEN VIEW IS RENDERED
// I've tried .[], .#each, .length... none of them worked and I believe #each.firstName would be the most appropriated from what I've read
console.log('computed method not showing',this.get('selected_people'));
return this.get('selected_people');
}),
init() {
this._super(...arguments);
this.set('selected_people',[]);
},
actions: {
updateList(item, moveToList) {
let removeFromList, fromList, toList;
if (moveToList === 'people') {
removeFromList = 'selected_people'
} else if (moveToList === "selected_people") {
removeFromList = 'people';
}
// get the object lists
fromList = this.get(removeFromList);
toList = this.get(moveToList);
// update list ---> HERE I UPDATE USING KOVL METHOD
toList.pushObjects(item);
fromList.removeObjects(item);
console.log('update!',this.get('selected_people'));
}
}

Just make sure tableModel should be accessed/required by the template or in code.
Computed property is lazy, so it will be calculated when you ask for it either inside the code or in a template. otherwise, it will not be called.
First time when we call, it will return result and it will be cached. and subsequent access will get it from cache. Changing any of the dependent properties causes the cache to invalidate so that the computed function runs again on the next access.
Mostlty copied from guides.emberjs

Related

Vue2 change object property from method param passing into child props

oi, this one seems so simple but it's giving me a headache.
I have a child component with a property passed down:
<dialog-child requests='requests'/>
the passed prop, is an object obtaining varied booleans. The dialog is v-modeled to in this case,
<dialog v-model='request.deleteItem'>
requests { deleteItem: false, editItem: false, syncItem: false, }
When I click on the delete button, I want to make a request to delete an item, and pull up this dialog component. This works fine if i simply change the bool in the object to true, but I need more control by passing the #click to a method and passing a parameter.
<btn #click='makeRequest(deleteItem)'>Activate Dialog</btn>
so in the method, I need to figure out how to say that the passed deleteItem, is request.deleteItem and then I would make it true.
makeRequest(requested){
//somewhow say
this.requests.requested = true
}
How could I pass in the parameter to take control of the objects property?
I could do a long form of multiple if checks, if requested = '' then make this prop true, but that feels gross.
I also need to pass in a second param, item after I figure this out - so to pass in two params do i just say methodName(param1, param2) and on click method(item1, item2) or do I need to create an object like method({item1, item2})?
To have everything nice and neat :) but:
For that we need option "to emit event on child component" but Vue.js does not work like that. So try this:
<template>
<div>
<dialog-child requests='requests' #resetRequest="resetRequest()"/>
<btn #click='makeRequest("deleteItem")'>Delete</btn>
</div>
</template>
<script>
import DialogChild from 'Dialog.vue'
export default {
components: { "dialog-child" : DialogChild },
data() {
return {
requests: {
makeRequest: false, //New Field
deleteItem: false,
editItem: false,
syncItem: false
}
}
},
methods: {
makeRequest(action) {
//So here you can create new request Object with whatever you want. Use action argument to check what you want to do here...
let newRequests = {
makeRequest: true,
deleteItem: true,
editItem: false,
syncItem: false
}
//Then this newRequest object need to copy to this.requests, this will update request object reference, and trigger "watch" in child component
this.requests = newRequests;
},
resetRequest() {
//Request object is again updated with new reference, but makeRequest is false so it will not trigger action in Dialog Child component
this.requests = {
makeRequest: false, //Now this is false,
deleteItem: false,
editItem: false,
syncItem: false
}
}
}
}
</script>
//And child component should be like this
<template>
<div>
</div>
</template>
<script>
export default {
props: {
requests: { Type: Object }
},
watch: {
requests(newVal) {
if(newVal && newVal.makeRequest) {
this.doStuff()
}
}
},
methods: {
doStuff() {
//So after doing what you want, you need to make event to reset requests
this.$emit('resetRequests');
}
}
}
</script>
i fixed this by changing the param to a string, and removing the request for a second item. Onwards to a item pass in too.

Vue 2 watch fails for mixin data property, but works if the component has the data property

I am trying to move some functionality to a vue mixin from the component, to be able to use it in multiple components.
This (simplified version of the code) works:
export default {
data() {
return {
file: {},
audioPlayer: {
sourceFile: null,
},
};
},
watch: {
'audioPlayer.SourceFile': function (nextFile) {
console.log('new sourceFile');
this.$data.file = nextFile;
},
}
}
But if I move the audioPlayer data object to a mixin, the watch does no longer fire.
Is this expected behavior?
N.b. I resolved this by directly making the 'file' data property into a computed value, which works in this particular case, but the behavior is still strange.
You need a lowercase s. sourceFile not SourceFile
watch: {
'audioPlayer.sourceFile': function (nextFile) {
console.log('new sourceFile');
this.$data.file = nextFile;
},
}

How to send data to component in vue js?

How do I send data to a component in Vue.js? I got a response from the server on the button click event, and now I want to send this response to the component and display on list using v-for.
Here is my code:
var store = new Vuex.Store({
state: {
Item: []
},
mutations: {
getItems: function (state) {
}
},
actions: {
fetchData:function (context) {
Vue.http.get('data.json').then(function(response){
alert('dd')
}, function(error){
console.log(error.statusText);
});
}
}
})
var httprequest = Vue.extend({
"template": '#http_template',
data: function () {
return {
items: store.state.Item
}
},
methods: {
fetchData: function () {
store.dispatch('fetchData')
},
}
})
Vue.component('httprequest', httprequest);
var app = new Vue({
el: '#App',
data: {},
});
You have almost done everything correct. Only thing you are missing is after getting data, you are not assigning it to state.Item. Please check the below code:
var store = new Vuex.Store({
state: {
Item: []
},
mutations: {
getItems: function(state, items) {
items.forEach(function(item) {
state.Item.push(item)
})
}
},
actions: {
fetchData: function(context) {
Vue.http.get('data.json').then(function(response) {
context.commit('getItems', response.data)
}, function(error) {
console.log(error.statusText);
});
}
}
})
working example can be found here.
You don't send data to components. You set up reactive pipes and the data moves around when it needs to. In your case, with vuex, you want to register store.state.items on the data of your component.
You can use a prop if you want, but you still need to do the registration in the parent's data. If your component is a singleton, intended for this page only, you're better registering what you need directly in the data of the component.
In general vue follows the principle that data goes the DOM tree down via properties and up via events. See for example https://v2.vuejs.org/v2/guide/index.html#Composing-with-Components.
Thus to get data into your component define a property myProp inside your component and when using your component bind it via v-bind:myProp="myData".
To get data back from your component use this.$emit('myUpdateEvent', myUpdatedData) and listen to the event by using v-on:myUpdateEvent="myUpdateHandler".

Ember js service and filterBy

i have a service to manage all the errors and alerts in my app. and the code looks like this
Service
import Ember from 'ember';
export default Ember.Service.extend({
messages: null,
init() {
this._super(...arguments);
this.set('messages', []);
},
add: function (severity, msg, messageType) {
if (severity === 'error') {severity = 'danger';}
var msgObject ={
severity: severity,
messageType: messageType,
msg: msg,
msgId: new Date()
};
this.get('messages').pushObject(msgObject);
},
remove(msgId) {
this.get('messages').removeObject(msgId);
},
empty() {
this.get('messages').clear();
}
});
Component
import Ember from 'ember';
export default Ember.Component.extend({
messageType:'global',
messageHandler: Ember.inject.service(),
messages: function(){
return this.get('messageHandler.messages').filterBy('messageType',this.get('messageType'));
}.property('messageHandler.messages'),
actions : {
dismissAllAlerts: function(){
this.get('messageHandler').empty();
},
dismissAlert: function(msgId){
this.get('messageHandler').remove(msgId);
}
}
});
Initializer
export function initialize(container, application) {
application.inject('component', 'messageHandler', 'service:message-handler');
}
export default {
name: 'message-handler',
initialize : initialize
};
Template
import Ember from 'ember';
export default Ember.Component.extend({
messageType:'global',
messageHandler: Ember.inject.service(),
messages: function(){
return this.get('messageHandler.messages');
}.property('messageHandler.messages'),
actions : {
dismissAllAlerts: function(){
this.get('messageHandler').empty();
},
dismissAlert: function(msgId){
this.get('messageHandler').remove(msgId);
}
}
});
and whenever there is an error i will add it like this
this.get('messageHandler').add('error',"Unable to get ossoi details","global");
my problem is the filterBy in the component is not working. if i remove the filterBy() it works and i can see the error in the template. am kinda new to ember so if anyone can help me figure out what am missing here or if there is a better way of doing this please let me know
filterBy usage is good and it should be working well. but messages computed property will not be recomputed whenever you add/remove item from messageHandler.messages.
messages: Ember.computed('messageHandler.messages.[]', function() {
return this.get('messageHandler.messages').filterBy('messageType', this.get('messageType'));
}),
In the above code I used messageHandler.messages.[] as dependant key for the messages computed property so that it will be called for add/remove items.
Refer:https://guides.emberjs.com/v2.13.0/object-model/computed-properties-and-aggregate-data/
Computed properties dependent on an array using the [] key will only
update if items are added to or removed from the array, or if the
array property is set to a different array.

Emberjs - Controller always gets default value from 'needs' controller

I have a situation where I have an application controller that 'needs' to access a property from another controller, like this:
App.ApplicationController = Ember.Controller.extend({
needs: ['sort'],
actions: {
appClicked: function () {
console.log('controllers.sort.isMenuExpanded');
}
}
});
App.SortController = Ember.Controller.extend({
isMenuExpanded: false,
actions: {
menuClicked: function () {
this.toggleProperty('isMenuExpanded');
}
}
})
As expected, the ApplicationController's appClicked function correctly logs 'false' the first time it is run. However, it continues logging false (the default value set in the SortController) even after the isMenuExpanded property has been changed to 'true' by the SortController.
This may have something to do with the way JavaScript passes values. I think objects are passed by reference, and sure enough, if I change the isMenuExpanded property to:
isMenuExpanded: { expanded: false }
and change the toggle to:
this.toggleProperty('isMenuExpanded.expanded');
the action in the ApplicationController correctly prints the isMenuExpanded.expanded value to the console every time it's updated.
Thanks in advance for your time and expertise!
Try setting it as a computed property.
App.ApplicationController = Ember.Controller.extend({
needs: ['sort'],
isMenuExpanded: Ember.computed.alias('controllers.sort.isMenuExpanded'),
actions: {
appClicked: function () {
console.log(this.get('isMenuExpanded'));
}
}
});
App.SortController = Ember.Controller.extend({
isMenuExpanded: false,
actions: {
menuClicked: function () {
this.toggleProperty('isMenuExpanded');
}
}
})

Categories