How to add more textfields in a view on clicking a button in the same view in sproutcore?
I have a sliding pane with particular number of textfields. On clicking a button, I need to add more number of text field in the same view.
Or,
I should be able to select the number from a select button view and show those many number of textfield in the same view.
I would recommend using a SC.ListView for this purpose.
You should have a SC.ArrayController whose content is an array containing objects that represent each text field. This may be as simple as something like this:
MyApp.myController = SC.ArrayController.create({
content: [
SC.Object.create({ someProperty: "Text field value 1" }),
SC.Object.create({ someProperty: "Text field value 2" }),
SC.Object.create({ someProperty: "Text field value 3" })
]
});
Next, you'll create your SC.ListView and bind it's content to the controller, and create the exampleView whose content is bound to the someProperty property of the objects:
MyApp.MyView = SC.View.extend({
childViews: 'scrollView addButtonView'.w(),
scrollView: SC.ScrollView.extend({
layout: { top: 0, left: 0, right: 0, bottom: 50 },
contentView: SC.ListView.extend({
contentBinding: 'MyApp.myController.arrangedObjects',
rowHeight: 40,
exampleView: SC.View.extend({
childViews: 'textFieldView'.w(),
textFieldView: SC.TextFieldView.extend({
// Add a little margin so it looks nice
layout: { left: 5, top: 5, right: 5, bottom: 5 },
valueBinding: 'parentView.content.someProperty'
})
})
})
}),
addButtonView: SC.ButtonView.extend({
layout: { centerX: 0, bottom: 10, width: 125, height: 24 },
title: "Add Text Field",
// NOTE: The following really should be handled by a statechart
// action; I have done it inline for simplicity.
action: function() {
MyApp.myController.pushObject(SC.Object.create({ value: "New Field" }));
}
})
});
Now, when you click the "Add Text Field" button, it will add a new object to the controller array which will automatically re-render the list view with the new object and hence, the new text field.
A couple of notes:
This uses a SC.ScrollView in conjunction with the SC.ListView, you will almost always want to do it this way.
Since we are using standard bindings (not SC.Binding.oneWay()), editing the text fields will automatically update the someProperty property in the objects in MyApp.myController and vice versa: if you update the value via some other means, the text field should automatically update as well.
This should not be used for a large list as using the childViews method of view layout can be slow. If you need performance, you should change the exampleView to a view that overrides the render() method and manually renders a text input and sets up the proper change events and bindings.
Lastly, I can't remember if the proper syntax for the text field's valueBinding is parentView.content.someProperty or .parentView.content.someProperty (notice the period at the beginning). If the first way doesn't work, try adding the . and see if that works.
Like Topher I'm assuming you're using SproutCore not Ember (formerly SC2).
If you need to add an arbitrary child view to an arbitrary location on your view, you want view.appendChild. In the button's action, you would do something like this:
this.get('parentView').appendChild(SC.View.create({ ... }))
If you go this route, you'll have to figure out the layout for the new view yourself. If you don't need to precisely control the layout, then go with Topher's solution - the ListView does the layout piece for you.
Related
I am working on one fabric js editor, in which I need to implement TextBox as a Button look and feel,
For that, I Have implemented TextBox with Background, But Problem is that Height of TextBox.
I am trying this kind of code :
var text = new fabric.Textbox("this is text", {
top: 0,
left: 0,
width: 300,
height: 500,
fill: 'red'
})
canvas.add(text);
Now my Textbox is rendered of width: 300 but height is not rendered. how can I set Height to My TextBox
And another Thing, when I set Height to TextBox, I need to keep text in Center of height (vertical center)
what should I do, I can't understand, how to override default fabtic.Textbox class.
Please help me, If any one have Any suggestion.
I have a div element which is made jquery Resizable. It has alsoResize option set, so other elements resize simultaneously.
What I want to do, is to set size of this Resizable div element programmatically in such way, that all Resizable logic is triggered (especially this alsoResize option is taken into account).
How can I achieve that?
Update: It looks like the internals of jQuery UI have changed dramatically since I answered this and firing the event no longer works.
There's no direct way to fire the event anymore because the resizable plugin has been fundamentally changed. It resizes as the mouse is dragged rather than syncing items up at the end. This happens by it listening for the internal resize propagation event for resizable plugins which is now fired by the _mouseDrag handler. But it depends on variables set along the way, so just firing that even internally won't help.
This means even overriding it is messy at best. I'd recommend just manually resizing the alsoResize elements directly, independent of the UI widget altogether if that's possible.
But for fun let's say it isn't. The problem is that the internals of the plugin set various properties relating to previous and current mouse position in order to know how much to resize by. We can abuse use that to add a method to the widget, like this:
$.widget("ui.resizable", $.ui.resizable, {
resizeTo: function(newSize) {
var start = new $.Event("mousedown", { pageX: 0, pageY: 0 });
this._mouseStart(start);
this.axis = 'se';
var end = new $.Event("mouseup", {
pageX: newSize.width - this.originalSize.width,
pageY: newSize.height - this.originalSize.height
});
this._mouseDrag(end);
this._mouseStop(end);
}
});
This is just creating the mouse events that the resizable widget is looking for and firing those. If you wanted to do something like resizeBy it'd be an even simpler end since all we care about is the delta:
var end = $.Event("mouseup", { pageX: newSize.width, pageY: newSize.height });
You'd call the $.widget() method after jQuery UI and before creating your .resizable() instances and they'll all have a resizeTo method. That part doesn't change, it's just:
$(".selector").resizable({ alsoResize: ".other-selector" });
Then to resize, you'd call that new resizeTo method like this:
$(".selector").resizable("resizeTo", { height: 100, width: 200 });
This would act as if you instantly dragged it to that size. There are of course a few gotchas here:
The "se" axis is assuming you want resize by the bottom right - I picked this because it's by far the most common scenario, but you could just make it a parameter.
We're hooking into the internal events a bit, but I'm intentionally using as few internal implementation details as possible, so that this is less likely to break in the future.
It could absolutely break in future versions of jQuery UI, I've only tried to minimize the chances of that.
You can play with it in action with a fiddle here and the resizeBy version here.
Original answer:
You can do this:
$(".selector").trigger("resize");
alsoResize internally rigs up a handler to the resize event, so you just need to invoke that :)
You can trigger the bars programmatically. For example, to trigger the east-west resize event:
var elem =... // Your ui-resizable element
var eastbar = elem.find(".ui-resizable-handle.ui-resizable-e").first();
var pageX = eastbar.offset().left;
var pageY = eastbar.offset().top;
(eastbar.trigger("mouseover")
.trigger({ type: "mousedown", which: 1, pageX: pageX, pageY: pageY })
.trigger({ type: "mousemove", which: 1, pageX: pageX - 1, pageY: pageY })
.trigger({ type: "mousemove", which: 1, pageX: pageX, pageY: pageY })
.trigger({ type: "mouseup", which: 1, pageX: pageX, pageY: pageY }));
I am doing a 1px left followed by 1px right movement on the east bar handle.
To perform a full size, you can target .ui-resizable-handle.ui-resizable-se if you have east and south resize bars.
I needed the same thing for tests. Similar questions have only one promising answer https://stackoverflow.com/a/17099382/1235394, but it requires additional setup, so I ended with my own solution.
I have an element with resizable right edge
$nameHeader.resizable({handles: 'e', ... });
and I needed to trigger all callbacks during the test in order to resize all elements properly. The key part of test code:
var $nameHeader = $list.find('.list-header .name'),
$nameCell = $list.find('.list-body .name');
ok($nameHeader.hasClass('ui-resizable'), 'Name header should be resizable');
equal($nameCell.outerWidth(), 300, 'Initial width of Name column');
// retrieve instance of resizable widget
var instance = $nameHeader.data('ui-resizable'),
position = $nameHeader.position(),
width = $nameHeader.outerWidth();
ok(instance, 'Instance of resizable widget should exist');
// mouseover initializes instance.axis to 'e'
instance._handles.trigger('mouseover');
// start dragging, fires `start` callback
instance._mouseStart({pageX: position.left + width, pageY: position.top});
// drag 50px to the right, fires `resize` callback
instance._mouseDrag({pageX: position.left + width + 50, pageY: position.top});
// stop dragging, fires `stop` callback
instance._mouseStop({pageX: position.left + width + 50, pageY: position.top});
// ensure width of linked element is changed after resizing
equal($nameCell.outerWidth(), 350, 'Name column width should change');
Of course this code is brittle and may break when widget implementation changes.
Hack Disclaimer (tested on jQuery 1.12.4):
This basically waits for the dialog to be opened and then increments by 1px (which forces the resize() event) and then decrements by 1px (to regain original size)
just say this in the dialog open event handler:
$(this)
.dialog("option","width",$(this).dialog("option","width")+1)
.dialog("option","width",$(this).dialog("option","width")-1);
note:
This may not work with show effects (like fadeIn,slideDown etc) as the "resizer" code executes before the dialog is fully rendered.
$(".yourWindow").each(function(e) {
$(this).height($(this).find(".yourContent").height());
});
And the same with the width.
dynamically set the header menudata like columns below
headermenu: { id: "headerMenu", css: "webix-contextmenu", width: 150, data: getHeaderMenuData(),
}
Like setting externally for columns, function add_column(){ var columns = webix.toArray(grid.config.columns); columns.insertAt({ id:"c"+webix.uid(), header:"New column" },2); grid.refreshColumns(); }
is there a way that I can set for headerMenu data.
Normally the header menu will regenerated self after refreshColumns call, and will include the newly added column. ( you may need to update to the latest version of Webix )
Also, you can add items to header menu directly, by using code like next
var menu_id = grid.config.headermenu;
$$(menu_id).add({
id:"new", value:"NEW"
});
I'm creating an application that receives a dynamic and recursive data structure, ie, an element can have an identical element within it, something similar to the HTML´s DOM.
At this stage I have "simple" elements type text, date, buttons, and others likes: grid, tables and sections. These last ones, can have a elements inside is structure, something like childrens, for example, a grid element can have other grid.
These models elements are already being built on a framework through my JSON received, usign a Factory pattern. In that order, i have already a list of elements to be drawn in the view.
My first approach was to implement a UICollectionView, but now i am having trouble drawing elements belongings to others.
Any idea what the best approach to solve this problem? always taking into account the performance of the app.
Thanks in advance
Beware of auto layout in such complex layouts. Auto layout has a cost, and the more views and more complex the scene is, the more toll it takes. Adding and removing constraints is costly, and layout passes themselves are costly.
Looks to me a rather simple layout where a collection view is placed inside a table view. Main view seems like a table view (can be a collection but is simple for that). Each element is a cell. Since you tell the table view the cell size, you can have whatever you want inside as long as you can calculate the size. In fact, the section element to me looks like the same table as the main table. Grid element is a collection view. You could do it as a grid view, and then you can use more reuse.
So let's take a look. Simple elements first, text element, date element, title section and footer section are either simple table view cells or collection view cells. Table element looks like a collection view to me but can be anything custom. A grid is a collection view with a flow layout. If you implement everything with collections, you can reuse the same classes for text and date elements as the ones in the main view, otherwise you use collection view cells here. Section element is a cell with a table view. The cell can act as a data source of the table view, or there can be a "nested table controller" which the cell can use as data source of the table view. To get the height of the section element cell, you can get the table view's content size's height and return that. If we further consider, the main view is basically a top level section element table. If you pick the "nested table controller" root, your main view controller can have that as a datasource of the main table view.
To track elements in the section nested view controller (or cell-as-data-source model), I recommend having an array of data representing, and an enum to determine the type. This way you can design the various types and select the cell identifiers based on the enum.
A crude object representation of this backing data structure would look like this:
{
type: Section
items: {
{
type: Text
items: {}
},
{
type: Date
items: {}
},
{
type: Grid
items: {
{
type: Text
items: {}
},
{
type: Date
items: {}
},
{
type: Text
items: {}
},
{
type: Text
items: {}
},
{
type: Date
items: {}
}
}
},
{
type: Section
items: {
{
type: Title
items: {}
},
{
type: Text
items: {}
},
{
type: Grid
items: {
{
type: Text
items: {}
},
{
type: Text
items: {}
},
{
type: Text
items: {}
}
}
}
{
type: Text
items: {}
},
{
type: Footer
items: {}
},
}
},
{
type: Table
items: {
...
}
}
}
}
This gives you the same example as your above example scheme.
Parsing this is very simple. At the top, you can either always assume there is a section element, or, even better, just iterate the top level items and use each item's type and in cellForRow: return an identifier according to the type, and if a complex type, pass the array of sub-items to the cell, who would perform the same logic and so on.
I used a table view for the vertical flow because they are easier to manager. You can use a collection view with a vertical flow layout as well.
Using iOS7's table view estimatedRowHeight, you could even not parse everything but only the most immediate cells for optimization, and create more and more as the user scrolls.
I am not 100% sure about your questions but sticking to your 'how can I create dynamic layout supporting recursion', I'd advise the following
Create a UIView subclass
Give it a model property (e.g. #property GridModel model) or give it an initialiser method with model (e.g initWithModel:(GridModel *)model)
In either drawRect method or initialiser methods, make it draw its subviews based on the model
In your code, parse the json and according to the type of object, initialise this special subview and make it draw
To draw many subviews dynamically and recursively, keep track of your y offset and make sure next subview starts from where the previous subview ends
Does this make sense?
What I want to do is track the (top, left, width, height, zIndex) style attributes. But since the element that holds these styles doesn't exist until after third party code gets pulled in, i can't do it the normal way.
Here's what I've got thus far:
my.Module = function (module) {
return {
top: ko.observable(200);,
left: ko.observable(100);,
width: ko.observable(100);,
height: ko.observable(100);,
zIndex: ko.observable(0);,
};
};
This would be the normal binding context:
<div data-bind="style:{width:width, top:top, left:left, height:height, zindex:zIndex}"></div>
But the third party stuff ends up wrapping the div i pass it within another, and this is something I cannot change. So instead I've started creating a custom bindinghandler:
ko.bindingHandlers.Window = {
init: function (element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
//Do third party creation stuff here...
ko.applyBindingsToNode(elemToActuallyWatch, {
style: {
width: value.width() + 'px',
height: value.height() + 'px',
top: value.top() + 'px',
left: value.left() + 'px',
zIndex: value.zIndex()
}
});
},
Now this applies the above styles to the appropriate div, but i still need the above values to change when both the data changes and when the element changes.
I feel like the 'px' that i have to tack on the ends shouldn't be needed but that was the only way i could get them to apply.
Is there a way from this point on, that lets say I'm using jqueryUI draggable and resizable, and a user moves/resizes the element that the values will automatically update the viewmodel data? Of course a non jquery specific answer would be best.
style is a one-way binding, from model to view. It won't update the model from the view. You'll probably need to use the resize option and update the model from there.