Hi I'm trying to make 3 way binding work on objects opened in input fields (Doesn't have to be Object)
The idea is to open an object in a div with the results of the object inside input fields, and to change the values of that object one would just need to change the data in those fields and the changes would be made to the objects values directly.
Anyone done something like this?
EDIT
I have done the simpler guides on firebase.com but for my project there is several fields on the object, I deleted the code I tried with but it was something similar to this.
Controller
$scope.loadModel = function($id) {
var rec = $firebaseObject(new Firebase('https://****.firebaseio.com/models/' + $id));
rec.$loaded().then(function(rec){
var record = rec;
$scope.record = record;
console.log(record.name);
var child = rec.client.id;
record.$bindTo($scope, "record").then(function() {
console.log($scope.data);
$scope.data.foo = "baz";
record.set({ foo: "baz" });
});
var obj = $firebaseObject(new Firebase('https://dam-db.firebaseio.com/clients/'+child));
obj.$loaded().then(function(obj){
$scope.object = obj;
console.log(obj.name);
//return obj;
})
})
};
Html
<div ng-model="record" ng-repeat="record in record">
<input value="{{record}}"></input>
</div>
Related
I have the following component to quickly configure stops on a delivery/pickup route and how many items are picked up and dropped
and this is the data model, note the 2 is the one next to 'a' on the previous image.
If a click the + or - button, in the first item, it behaves as expected,
But second item doesn't work as expected
I've already checke a couple of posts on object property update likes this ones
Is it possible to mutate properties from an arbitrarily nested child component in vue.js without having a chain of events in the entire hierarchy?
https://forum.vuejs.org/t/nested-props-mutations-hell-internet-need-clarification/99346
https://forum.vuejs.org/t/is-mutating-object-props-bad-practice/17448
among others, and came up with this code:
ADD_ITEM_TO_SELECTED_STOP(state, payload) {
let count = state.selectedStop.categories[payload.catIndex].items[payload.itemIndex].count;
const selectedCat = state.selectedStop.categories[payload.catIndex];
const currentItem = selectedCat.items[payload.itemIndex];
currentItem.count = count + 1;
selectedCat.items[payload.itemIndex] = currentItem;
Vue.set(state.selectedStop.categories, payload.catIndex, selectedCat);
},
and as the button event:
addToItem(item) {
this.$store.dispatch("addItemToSelectedStop", {
catIndex: item.catIndex,
itemIndex: item.itemIndex
})
},
And finally my computed property code:
items() {
let finalArray = [];
this.selectedStop.categories.forEach(
(cat, catIndex) => {
let selected = cat.items.filter((item) => item.count > 0 );
if (selected.length > 0) {
//here we add the catIndex and itemIndex to have it calling the rigth shit
selected = selected.map(val => {
let itemIndex = cat.items.findIndex( itemToFind => itemToFind.id === val.id);
return {
...val,
catIndex: catIndex,
itemIndex: itemIndex,
}})
finalArray = finalArray.concat(selected);
}
});
return finalArray;
}
What confuses me the most is that I have almost the same code in another component, and there it's working as expected, and although the model is changed, the computed property is only recalculated on the first item,
After reading this gist and taking a look again at the posts describing this kind of issue, I decided to give it a try and just make a copy of the whole stored object not just the property, update it, then set it back on vuex using Vue.set, and that did the trick, everything is now working as expected, this is my final store method.
ADD_ITEM_TO_SELECTED_STOP(state, payload) {
let selectedLocalStop = JSON.parse(JSON.stringify(state.selectedStop));
let count = selectedLocalStop.categories[payload.catIndex].items[payload.itemIndex].count;
selectedLocalStop.categories[payload.catIndex].items[payload.itemIndex].count = count + 1;
Vue.set(state,"selectedStop", selectedLocalStop );
//Now we search for this step on the main list
const stepIndex = state.stops.findIndex(val => val.id === selectedLocalStop.id);
Vue.set(state.stops,stepIndex, selectedLocalStop );
},
I had to add the last bit after updating the whole object, because, originally, the array items were updated when the selected item was changed, I guess some sort of reference, but with the object creation, that relationship no longer works "automatic" so I need to update the array by hand
I am trying to paginate my table.
I have the full implementation of Angular Material Table with Pagination(https://material.angular.io/components/table/overview#pagination)
Since I am using Angularfire2 my dataSource is an Observable.
this.items = this.itemsCollection.valueChanges();
I read that I have to use
dataSource = new MatTableDataSource<Item>;
to create a working connection between table and paginator.
I populate the dataSource by
let subscription = this.items.subscribe(
newData => { this.dataSource.data = newData });
and update the dataSource's paginator by
ngAfterViewInit() {
this.dataSource.paginator = this.paginator;
}
where this.paginator is defined by
#ViewChild(MatPaginator) paginator: MatPaginator
like explained in Angular Material's components doc.
The paginator(mat-paginator) is working fine. However, my issue is that the table does not refresh and continues to show all values. Initial values to 'this.paginator' are also not passed to the table limit.
I found a known bug, but I am not sure if that applies to my problem...
Can anyone help me out?
Thank you in advanced!!
Update
I found an easy solution for my problem by using the Angular Pipe at mat-table. I added the slice pipe like this:
<mat-table #table [dataSource]="dataObservable | async | slice: a:b">
and in my data component I was listening to the change event of the HTML Element "mat-paginator"
(page)="changePage($event)"
In my component I can easily set the sliced data
a = 0;
b = this.limit;
changePage(ev) {
console.log( ev );
this.a = ev.pageIndex * ev.pageSize;
this.b = this.a + ev.pageSize;
console.log(this.a);
console.log(this.b);
}
I had a List that used to work when it was bound directly to a store but now I want that list to get it's data from a queryBy on the original store.
Looking at the documentation is seems like setItems should do what I want.
var myStore = Ext.getStore('myStoreData');
var myData = myStore.queryBy(function(item) {
return item.get('status') !== null;
});
// At this point myData looks valid and has the data I want.
// Ext.apply.create.Class {all: Array[5], items: Array[5], keys: Array[5], indices: Object, map: Object…}
Ext.getCmp('myListComponent').setItems(myData.items);
I keep getting the error "Object [object Object] has no method 'getItemId'". I tried various other incantations but without success. I also took a look at setData and add but without success.
========================
After getting Thiem's answer I just ended up creating a function that would create a filtered copy of an existing store and then just setting the List store to that. Code below for others edification...
storeCopy: function(store, filterBy) {
var records = [];
var allRecords = null;
if(filterBy)
allRecords= store.queryBy(filterBy);
else
allRecords= store.queryBy(function(){return true;});
allRecords.each(function(r){
var rec = r.copy();
rec.setId(r.getId());
records.push(rec);
});
var store2 = new Ext.data.Store({
recordType: store.recordType
});
store2.add(records);
return store2;
},
Thanks all.
setItems method does a totally different thing. For example, says you have an Ext.Container which consists of a form, some fields, and some interaction buttons. These things are call child components, or items of the container. They are oftenly declared in the items config of the parent container and setItems is designed to programmatically set the value of that config. So it has nothing to do with the store logic.
In your situation, here is one of the solutions:
Create a store instance which contains filtered data.
Use this command: yourList.setStore('yourFilteredStore')
And it should reload... hope this helps
I've got a view that iterates a collection and calls DisplayFor() for each element in the collection.
I need to manually iterate (as opposed to passing the collection to DisplayFor) in order to tell the template if a break in the list should be drawn. The items in the list will only be of 2 types, ordered by them, so I only need to show this break once.
My template is found and called correctly.
I can see the HTML it generates correctly, ie: DisplayFor().ToHtmlString()
I can set this HTML as a scoped variable, ie: var html = DisplayFor().ToHtmlString() ..
But even Html.Raw(html) does not render it in the browser - the HTML has simply vanished.
What's going on?
var renderBreakInList = Model.Items.Any(x => x.IsSomeType);
foreach(var item in Model.Items)
{
var renderBreak = renderBreakInList && item.IsOtherType;
Html.DisplayFor(x => item, new { renderBreak = renderBreak });
if (renderBreak)
{
renderBreakInList = false;
}
}
The Html.DisplayFor method in itself does not render anything to the response just returns the generated HTML as a MvcHtmlString.
In order to actually write the rendered HTML to the response you need to tell this to Razor with using the # sign:
#Html.DisplayFor(x => item, new { renderBreak = renderBreak })
So your whole code should look like this:
#{
var renderBreakInList = Model.Items.Any(x => x.IsSomeType);
foreach(var item in Model.Items)
{
var renderBreak = renderBreakInList && item.IsOtherType;
#Html.DisplayFor(x => item, new { renderBreak = renderBreak })
if (renderBreak)
{
renderBreakInList = false;
}
}
}
Or you can use the WebPageBase.Write method (which gets called under the hood when using the # sign):
Write(Html.DisplayFor(x => item, new { renderBreak = renderBreak }));
finally figured this out after trying a lot of different things and reworking how I tell the Template to draw the break.
Rather than send a bool which I'd prefer to make the template more robust (if the order changes), I'm passing in the ID of the item that should draw the break.
#{
var breakItem = Model.Items.FirstOrDefault(x => renderBreakInList && x.IsSomeType);
var breakID = breakItem == null ? (long?)null : (long)breakItem.ID;
}
#Html.DisplayFor(x => x.Items, new { breakID = breakID })
Also like nemesv pointed out, Html.DisplayFor() needs to be prepended with #. I got out of the habit of doing this inside code blocks because I would always get the 'once inside a code block you don't need #` error..
Is there a simpler list type than DataGrid that can be connected to a store for Dojo?
I would like the data abstraction of the store, but I don't need the header and cell stucture. I would like to be more flexible in the representation of the datalines, where maybe each line calls an function to get laid out...
You ask a really good question. I actually have a blog post that is still in draft form called "The DataGrid should not be your first option".
I have done a couple thing using the store to display data from a store in a repeated form.
I have manually built an html table using dom-construct and for each.
var table = dojo.create('table', {}, parentNode);
var tbody = dojo.create('tbody', {}, table); // a version of IE needs this or it won't render the table
store.fetch({ // this is a dojo.data.ItemFileReadStore, but you cana dapt to the dojo.Store API
query: {},
onComplete: function(itms) {
dojo.forEach(itms, function(itm, idx) {
var tr = dojo.create('tr', {}, tbody);
// use idx to set odd/even css class
// create tds and the data that goes in them
});
}
});
I have also created a repeater, where I have an html template in a string form and use that to instantiate html for each row.
var htmlTemplate = '<div>${name}</div>'; // assumes name is in the data item
store.fetch({ // this is a dojo.data.ItemFileReadStore, but you cana dapt to the dojo.Store API
query: {},
onComplete: function(itms) {
dojo.forEach(itms, function(itm, idx) {
var expandedHtml = dojo.replace(htmlTemplate, itm);
// use dojo.place to put the html where you want it
});
}
});
You could also have a widget that you instantiate for each item.