Why are new rows not being added to ag-grid using vuejs? - javascript

I've got an ag-grid component in a vue.js application declared like this:
<ag-grid-vue :enable-sorting="false"
:enable-filtering="false"
:suppressDragLeaveHidesColumns="true"
:rowData="category.policyDocuments"
:gridOptions="policyGridOptions"
#cellValueChanged="savePolicyRow"
#grid-ready="onPolicyGridReady($event, category)"
class="w-100"
style="height: 200px">
</ag-grid-vue>
Bound to this typescript view model:
export class PolicyListViewModel {
status: PolicyDashboardStatus;
policyLists: DocumentWalletPolicyDocumentList[] = [];
gridApi: GridApi | null = null;
policyDocuments: DocumentWalletDocumentViewModel[] = [];
constructor(status: PolicyDashboardStatus) {
this.status = status;
}
get shouldShow(): boolean {
return this.policyDocuments.length > 0;
}
updateDocuments(): void {
this.policyDocuments = this.policyLists
.map(list => list.policyDocuments)
.reduce((a, b) => a.concat(b), []);
}
}
When my page first displays I get the right data. When I call updateDocuments with new data the grid does not update. I have verified in vue devtools that the rowData prop is being updated. ag-grid documentation would suggest that the component should react to the change. Any ideas what I am doing wrong?

Ah-ha. Finally figured it out, posting here in case it helps anyone else.
It looks like my problem was binding both gridOptions and rowData at the same time. Everything was working fine except for the the refresh when rowData was changed. I'm assuming this is because the grid options had its own rowData property and the ag-grid wrapper was using the data from one copy and detecting changes on the other (or something like that).
I replaced the gridOptions binding with a columnDefs binding (in this case the only options I needed to set) and everything started working exactly as it should.

Related

How can I bind a text box to a complex object within state in React?

Background
I am building an office add-in using their React-based starter kit and TypeScript. I mention this because I am unable to get great debug support as far as I can tell, so I'm unable to see the error message that React is providing in my current situation.
What I'm attempting
(simplifying below. I can be more specific if you'd like; let me know.)
I have an interface for my AppState, and a complex object with some properties:
export interface AppState {
eventInput: EventInput;
}
export class BookendEventInput {
public minutesAdjacent: number = 30;
public subject: string = "";
public enabled: boolean = false;
public eventId: string = "";
}
I have one working scenario, which is a checkbox:
<Checkbox id="enableBookendBefore" checked={this.state.eventInput.enabled} onChange={this.eventInputCheckboxChanged}></Checkbox>
That is updating the state via the change function:
eventInputCheckboxChanged = () => {
this.setState((state: AppState) => {
var newValue = !this.state.eventInput.enabled;
var input = state.eventInput;
input.enabled = newValue;
state.eventInput = input;
})
}
But this isn't working for another scenario.
The Problem
I am now attempting to do something similar with a textbox. I have an input:
<input type="text" id="subject" disabled={!this.state.eventInput.enabled} value={this.state.eventInput.subject} onChange={this.subjectChanged} />
And the change function:
subjectChanged = (e) => {
var newSubject = e.target.value;
this.setState((state: AppState)=> {
var input = state.eventInput;
input.subject = newSubject;
state.eventInput = input;
})
Expected Behavior: I would expect to see the subject text box & state updated, as if they were two-way bound.
Actual Behavior: The entire screen goes blank/white, indicating that I'm getting a React-level error I believe (since I can't do F12 and can't see debug output due to it being in a task pane inside Outlook.)
The Question
How can I correctly bind a textbox using React, that's tied to a property in an object within state? Is it possible to do this, or am I violating a React principle?
In this case, you're using the callback to setState to try and modify state. This is either not firing or causing an infinite loop, I'm unsure of which!
Either way, to correctly modify state you'll want:
subjectChanged = (e) => {
var newSubject = e.target.value;
var input = state.eventInput;
input.subject = newSubject;
this.setState({eventInput: input});
});
This will achieve what you're looking for.

Angular 2 - NgFor not updating view

I am working on angular 2 project and I am having an issue when I am trying to change the list . NgFor not recognizing the changes , and displaying only the list loaded at first time .
here is an example code when I am loading all list and imminently after loading I reset it with null . the view still displaying all the list ...
this is my component constructor for example :
constructor( private songService : SongService)
this.songService.getSongs()
.subscribe(songsList => {
this.songs = songsList;
});
this.songs = null;
}
and this is the html :
<div class="row">
<div *ngFor= "let song of songs" class="col-md-4">
<app-song-item [song]="song"></app-song-item>
<br>
</div>
</div>
Loops in Angular sometimes screw up, in the way that they don't track your items the way you would want it to.
To prevent that, you can use a custom track by function like this
<div *ngFor="let song of songs; let i = index; trackBy: customTB" class="col-md-4">
In your TS
customTB(index, song) { return `${index}-${song.id}`; }
This way, you set up a custom trackBy function, that will update your view (the view wasn't getting updated because the tracking ID wasn't changing).
The reason why you are still seeing your list is because it is async. You can't be sure when the subscribe method is executed. It can be be direct, within seconds, take hours or not even at all. So in your case you are resetting the list before you are even getting one.
constructor( private songService : SongService)
this.songService.getSongs()
.subscribe(songsList => { //Might take a while before executed.
this.songs = songsList;
});
this.songs = null; //executed directly
}
The above explanation might be the cause of your problem, but there could also be another explanation. The constructor is only called when the component is created. Changing a router parameter doesn't necessarily create a component. Angular might re-use the component if it can.
Instead of null you should set an empty array, also have it inside a method, otherwise it never gets called
this.songService.getSongs()
.subscribe(songsList => {
this.songs = songsList;
});
clear(){
this.songs = [];
}
Try this
constructor(private songService: SongService) {
this.songService.getSongs()
.subscribe(songsList => {
this.songs = songsList;
this.reset();
});
}
reset() {
this.songs = [];
}

Vuejs template not updating applying filter to property

I have a simple template that iterates over some items:
<template v-for="card in filteredCards">
filteredCards are a property I am using to filter some results by clicking a simple html link. In my Vue component I have this:
data = {
cards: '',
filteredCards: ''
}
cards is the original data coming via an ajax request - and filteredCards is what I'm actually iterating over.
The problem becomes when I do any kind of update with a filter - the template is not reflecting the filtered array. Here is how I'm filtering:
this.filteredCards = this.cards.filter(function (item)
{
return item.Data.event_type.match('something_test');
});
In devtools I can see that the array has been updated to only a single item - however the template never updates and leaves all the results showing. If I call something that actually mutates the array though like reverse - the template updates just fine. Is there something I need to do in order to force an update after filtering the array?
I've updated a bit to reflect using a custom filter. I'm still running into the same problem. In devtools I see that the filterKey is being updated from an event broadcasted from the parent instance. But nothing is being updated in the template.
var $data = {
cards: [],
filterKey: '',
loading: true
};
Vue.component('cards', {
template: '#card-template',
data: function()
{
return $data;
},
events: {
'updateFilterKey': function(key)
{
this.filterKey = key;
}
}
});
Vue.filter('onlyMatching', function(cards)
{
var $this = this;
return cards.filter(function(item)
{
return item.Data.event_type.match($this.$data.filterKey);
});
});
The code that initially gets the data is just a simple ajax call:
var getFeed = function($url)
{
$.get($url, function(response)
{
$data.loading = false;
$data.cards = response;
}).fail(function()
{
$data.loading = false;
});
};
The strange thing is with this current code - when I click back and forth between sending different keys the actual array items are being duplicated in my template over and over when I click the "all items" which sets the filterKey to an empty string.
What you're doing is absolutely backwards.
You actually want to loop the original array but apply a filter on it.
<template v-for="card in cards | filterBy onlyMatching">
Then, in your code, create a custom filter:
Vue.filter('onlyMatching', function (cards) {
return infoBlocs.filter(function(item) {
return item.Data.event_type.match('something_test');
});
})
This should totally work. Now, whenever the array is changed in any way, the filter will get triggered and the list will be re-rendered.
Come to find out the filtering was working all along. There's an issue with the conditional templating. I'll ask that in another question.

How to dynamically append a record into an Array in flex?

I have an mxml view in flex, and I need to dynamically add data to a DataGrid component.
This is where the DataGrid is initialized:
<mx:DataGrid id="myGrid" width="100%"
dataProvider="{initDG}" >
<mx:columns>
<mx:DataGridColumn dataField="Identifier" />
<mx:DataGridColumn dataField="Name" />
</mx:columns>
</mx:DataGrid>
This is the script part:
private var DGArray:Array = new Array;
[Bindable]
public var initDG:ArrayCollection;
private function onCreation():void{
initData();
}
public function initData():void {
initDG=new ArrayCollection(DGArray);
}
private function onShow():void{
for (var child:Object in children) {
var details:Array = null;
if (child instanceof String) {
var test:String = children[child].toString();
details = test.split(",");
}
//Here I need to make an object like this one:
// record = {Identifier: details[0] , Name: details[1]};
this.DGArray.push(the record created);
}
}
I did this method because it's working if DGArray was a static Array:
private var DGArray:Array = [
{Identifier:'001', Name:'Slanted and Enchanted'},
{Identifier:'002', NAme:'Brighten the Corners'}];
Can anyone tell me how to create the record and add it to DGArray?
Thanks:)
In short
Add or remove items through the ArrayCollection instance instead of through the Array instance.
And here's why
ArrayCollection - as its name suggests - is in fact nothing but a wrapper around Array, adding some functionality to it that comes in handy when working with the Flex framework.
So when you do
initDG.addItem(theNewItem);
that new item will automatically also be added to the underlying Array.
Additionally this function will also dispatch a CollectionEvent, which will notify the DataGrid that the data in its dataProvider has changed and it should be redrawn to reflect those changes.
If on the other hand you do
DGArray.push(theNewItem);
like you did, you directly alter the underlying Array. This doesn't really break anything; you'll still be able to acces the new item through e.g. ArrayCollection.getItemAt() as well, but the DataGrid was never notified of the change, hence it didn't redraw and keeps displaying the old data.

ExtJS refreshing a view from the controller

I have the following controller in ExtJs:
Ext.define('FileBrowser.controller.BrowserController', {
extend: 'Ext.app.Controller',
views: ['browser.tree_dir', 'browser.grid_file'],
stores: ['store_dir', 'store_file'],
init: function () {
this.control({
'window > tree_dir': {
itemclick: {
fn: function (view, record, item, index, event) {
if (record.isLeaf() == false) {
Ext.getStore('store_file').load({
params: {
dir: record.data.id
}
});
var parentOfCurrentFiles = record.data.id
nodeId = record.data.id;
htmlId = item.id;
var grid_view = this.getView('browser.grid_file');
var grid_view_v = grid_view.getView();
grid_view_v.refresh();
}
}
}
}
});
},
onPanelRendered: function () {
console.log('The panel was rendered');
}
});
If you notice under 'itemclick' I am trying to refresh one of my views, my approach is not working. Can anyone explain to me how I can refresh the view? Thank you.
Replace var grid_view= this.getView('browser.grid_file'); with var grid_view= this.getView('browser.grid_file').create(); to get a real instance (as I already told you, getView() only return the view config, not a instance!) or if you have already created that grid and only one instance exist use the xtype along with a component query to receive it var grid_view=Ext.ComponentQuery('grid_file')[0]
Now to the refresh()
Basically you never need to call this method cause your grid is bound to a store and any change made on this store is directly reflected to your grid.
I would also recommend you to store view instances when creating them instead of using queries or directly use the ref property and let ExtJS do the work for you. The last one will the best solution you I guess... Take a look at ref's within the API examples and give it a try.
So what you are trying to do is, load the store and have the data reflect once you refresh the grid_view...?
In that case, you haven't done a setStore() to the grid, or if you have done that elsewhere, you are't doing a setData() to the store. Also you should call the refresh on the grid.

Categories