I am using a window view in Enyo, which basically fetches data from database, and based on the no. items fetched, multiple buttons are created dyanamically. On click of any of the button, an another call to database is made to fetch other set of items. The fetched items need to be added dyanamically to a <ul> item as buttons. Which is done by the code -
testPOSView : function(inSender, inEvent) {
var data = inEvent.data;
console.log(data.tables);
enyo.forEach(data.tables, function(table) {
console.log(table);
this.$.sectiontablebar.createComponent({
kind : 'OB.OBPOSPointOfSale.UI.TablesButton',
button : {
kind : 'OB.UI.Section',
content: table.tableName,
id: table.tableId
}
});
}, this);
}
But when i click on the button, i am getting the results from DB, but they are not added to the sectiontablebar component.
The complete code of the file is available # https://gist.github.com/sangramanand/ad665db9cd438001254a
Any help would be appreciated. Thanks!!!
I'm not certain this is the way to do it, but when I create subcomponents dynamically, I add this.render() to the end of the function. This renders the component, thus showing the dynamically added content.
If I were to rewrite your code I would do it like this:
testPOSView : function(inSender, inEvent) {
var data = inEvent.data;
enyo.forEach(data.tables, function(table) {
// create the sub-component in "this"
this.createComponent({
// and assign the container
container: this.$.sectiontablebar,
kind : 'OB.OBPOSPointOfSale.UI.TablesButton',
button : {
kind : 'OB.UI.Section',
content: table.tableName,
id: table.tableId
}
});
}, this);
this.render();
}
More than likely you are losing the pointer to this when testPOSView is running after an async call to get the db results.
In your anythingTap function (see gist, readers) you might try binding the function you're sending to OB.DS.Process:
this.bindSafely(function(data) {me.doTestPOSView();}))
By the way, you can probably eliminate all that me = this tomfoolery if you bind your functions properly.
Related
I'm using ag-grid (free) with Angular 1 and I've already gotten my tree data to display as desired, where the children of a node are in the column to the right of it. However, what I want to do is collapse or expand nodes on double click. Right now just focusing on getting them to collapse since my default view is set to expand. here's my code for the double click event, given within $scope.gridOptions:
onCellDoubleClicked: function(event){
event.node.expanded = false;
$scope.gridOptions.api.refreshView();
};
My assumption was that changing the expanded property to false would cause the refreshView call to re-render the grid with child nodes collapsed, but the view is unchanged after the double click.
Also, my getChildNodeDetails within gridOptions:
getNodeChildDetails: function(obj){
if (obj.children){
var nodeType = obj.breakdownCol;
return {
group: true,
expanded: obj.expanded || true,
children: obj.children,
field: 'name',
key: obj[nodeType]
}
} else {
return null;
}
}
Any ideas on how I might fix this without buying enterprise? I know that in enterprise you can group the rows and this comes with build in expand/collapse functionality.
In my own application I created a work around that simulates the row grouping feature. What it really does is adds and removes the data from the grid.
One drawback that this option has is that since the rows aren't actually in the table any filtering or sorting on columns can't actually take place on data that isn't shown, unlike the actual enterprise feature that the grid offers. However if you have disabled filtering and sorting then this option is perfectly viable.
Something like this:
function toggleExpansion(index, data) {
if (insert) {
gridOptions.api.insertItemsAtIndex(index, data);
} else {
gridOptions.api.removeItems(data)
}
}
My specific code goes into more checks and other things unrelated to this question but that is the simple explanation of what I am doing as a work around.
I am using React, but you could probably do something similar with Angular:
function expandAll(expand) {
agGridRef.current.api.forEachNode((node) => {
node.setExpanded(expand);
});
}
where the agGridRef is a reference to the component:
<AgGridReact
ref={agGridRef}
.
.
.
</AgGridReact>
I have a Backbone View and I want restart it. The view prints a table with prices but when the user push a check, the prices have that change.
I don't know restart my backbone View.
GrillaView = Backbone.View.extend({
events : {
// events
},
initialize : function(){
// templates
},
render : function (){
this.$el.removeClass('hidden');
this.renderQuote();
},
setCategories : function (collection){
// Print a template
},
addResult : function (item){
// Print other template
},
setPrice : function (prov,item){
// prices
},
filterFuel : function(e){
// Here I need restart the View
}
Totally unclear question but I assume you want to rerender when new data are available. So when new data are here you'll trigger this.render(); inside your view.
the render function should get its data from a model, when the model is updated you trigger render(). It's that simple. But without more code it's hard to tell what's your problem.
let me know if it helps.
Is there a way to detect list change in canjs and make the view redraw? I am changing the list but this is not shown on screen.
At the moment i have view model
TodosListViewModel = can.Map.extend({
todoCreated: function(context, element) {
// new todo is created
var Todo = this.Todo;
new Todo({
name: can.trim(element.val())
}).save();
element.val("");
},
tagFiltered: function(context, element) {
// filter todos according to tag
this.todos = this.todos.filter(function(todo) {
return todo.tag === element.val();
});
}
});
And component
can.Component.extend({
// todos-list component
// lists todos
tag: "todos-list",
template: can.view("javascript_view/todos-list"),
scope: function() {
// make the scope for this component
return new TodosListViewModel({
todos: new TodoList({}),
Todo: Todo
});
},
events: {
"{scope.Todo} created": function(Todo, event, newTodo) {
// todo created
this.scope.attr("todos").push(newTodo);
},
"{scope.todos} changed": function(a,b,c,d,e,f,g,h) {
console.log("todo change",d,e);
}
}
});
The markup
<input type="text" name="tagFilter" placeholder="Tag lookup" can-enter="tagFiltered" />
The rest of code http://git.io/vrPCTQ
In the case you're showing in the fiddle, you haven't define "page" in the scope to take a raw string value from the component's tag (using "#" as the value for scope.page). Check out the one-line difference in router's scope here:
http://jsfiddle.net/tkd9Lvtm/3/
EDIT: That didn't address the original question, so here's what else you can do to get this started. I made a new fiddle version for you.
http://jsfiddle.net/tkd9Lvtm/4/
The best way with CanJS 2.1 to accomplish what you want is to use can-value attributes on your form fields to two-way bind your elements to attribute values on your view model. You can see that the input field for the tag search is now using can-value instead of can-change -- this makes it independent of the filter function, which is only used to draw the items farther down.
CanJS will automatically rerun the filter when the attribute changes, because calling this.attr("filterTerm") inside the view model's filter function sets up binding the first time it's run. The live bound view layer is making computes out of these functions "under the hood" and these computes (a) listen to changes on attributes that are read inside the function; and (b) updates the DOM with each change to listened-to attributes. Using the view model to store the value in the filter field then allows that function to fire again on each change.
Problem: Meteor JS app with 2 distinct templates that need to share some data.
They are dependent on one another, since I aim to extract text (Step 1) from one, and then create dynamic buttons (Step 2) in another template. The content of the buttons is dependent on the table.
buttons.html
<template name="buttons">
{{#each dynamicButtons }}
<button id="{{ name }}">{{ name }}</button>
{{/each}}
</template>
My goal is for the name property to come from the content of reactiveTable.html (see above, or their Github page, package meteor add aslagle:reactive-table.
These need to be dynamically linked since table re-renders constantly w/ different group of products, which are linked up through Template.reactiveTable and a specific data context (Pub/Sub pattern).
IF the table is (re)rendered, then parse it's content and extract text. Once the table is parsed, dynamically inject newly created buttons into the UI. Note UI.insert takes two arguments, the Object to insert, and then location (DOM node to render it in).
Template.reactiveTable.rendered = function () {
UI.insert( UI.render( Template.buttons ) , $('.reactive-table-filter').get(0) )
};
(Insert new buttons every time a reactiveTable is rendered.)
This code works, but is flawed since I cannot grab the newly rendered content from reactiveTable. As shown in this related question, using ReactiveDict package:
Template.buttons.helpers({
dynamicButtons: function() {
var words = UI._templateInstance().state.get('words');
return _.map(words, function(word) {
return {name: word};
});
}
});
Template.buttons.rendered = function() {
// won't work w/ $('.reactiveTable) since table not rendered yet, BUT
// using $('h1') grabs content and successfully rendered dynamicButtons!
var words = $('h1').map(function() {
return $(this).text();
});
this.state.set('words', _.uniq(words));
};
Template.buttons.created = function() {
this.state = new ReactiveDict;
};
How can I change my selector to extract content from Template.reactiveTable every time is re-renders to create buttons dynamically? Thanks.
You’re using a lot of undocumented functions in there, and UI.insert and UI.render which are bad practice. The just-released Meteor 0.9.1 eliminates them, in fact.
Create your dynamic buttons the Meteoric way: by making them dependent on a reactive resource. For example, a Session variable. (You could also use a client-side-only collection if you want.)
Template.reactiveTable.rendered = function () {
// Get words from wherever that data comes from
Session.set('buttons', words);
};
Template.buttons.helpers({
dynamicButtons: function() {
if (Session.equals('buttons', null))
return [];
else
return _.map(Session.get('buttons'), function(word) {
return {name: word};
});
}
});
Every time reactiveTable is rendered or rerendered, the buttons Session variable will update. And because your dynamic buttons are depending on it, and since Session variables are a reactive resource, the buttons will rerender automatically to reflect the changes.
In my app i have a few tagList, each one contains a few tags grouped by index_name.
I'm lost on how should i handle that with Backbone views.
I have a TagListView that extends Backbone.View, i guess i'll handle all events with this view.
My main question i : should i create a TagView with a render function that would be created & rendered for each tag in the TagListView render function ?
Here is what i do so far in my view : (is this ok for initialization ?!)
<ul id="strategy-tags">
<!-- initial data -->
<script type="text/javascript">
AppData.strategyTagList = $.parseJSON('<?php echo json_encode($strategyTagList); ?>');
strategyTagListView = new App.TagListView({el : "#strategy-tags", data: AppData.strategyTagList});
</script>
</ul>
Inside my core.js :
App.TagListView = Backbone.View.extend({
// dom event specific to this item.
events: {
},
initialize: function(options) {
this.el = options.el;
},
render: function() {
// let's construct the tag list from input data
_.each(options.data, function(index) {
// render index? <-- how ?
_.each(index, function(tag) {
// render tag? <-- how ?
console.log(tag);
});
});
return this;
}
});
Thanks a lot.
I would say yes, the benefit of the individual 'item' view being able to re-render individually is that if you make changes to the model behind such an item, only that item will need to be re-rendered by the browser. Which is best for performance.
It seems to be a question of granularity here, and one I've asked myself on occasion. My advice would be not to over do the views. It is akin to creating objects for everything in java - sometimes a simple string will suffice. If you find a case of increased granularity in the future you can always come back and change it.