Me and my team are developing a hybrid mobile app (ionic) that basically uses SQLite as a data source to build a totally dynamic form that users then fill out. Forms are build out of sections and sections are build out of fields. Each field is a separate custom directive (like input, dropdown, etc.) that receives the setup object (ui.config) on init. The directives are totally reusaable and they carry all the information to render (template, logic, validation, special functions, etc.). But... Some of the forms that we build with this app can easily reach 50 + fields per page so we often encounter a frozen UI. We have a lot of ng-repeat loops in the form itself and we tried to replace them with quick-ng-repeat, use bindeonce and built in ::, but no serious improvement was made. UI still stucks.
Our concept of building the form is something like this:
1st Loop: ng-repeat form in forms
2nd Loop: ng-repeat section in sections
3rd Loop: field in fields => custom directive (input control)
I was wondering if there is a way to render Angular templates before they are compiled. Our ng-repeats that build the template layout and logic are slow, so I am thinking that the only thing that would boost things up is to create a directive that builds the dynamic HTML and then calling $parse on it.
Does anybody have any experiences with huge forms with a lot of watchers? We need an advice on how to handle this.
Thanks!
Writing code outside the $digest seems the way when you have literally thousands of elements shown. It's a bit risky on the update part, you have to track down what to update - the rest is just peanuts.
You may feel the need to remove ng-mouse* or ng-click type of events too and have 1 single listener on the parent (you can still detect who's the author by the event.target).
As per another comment: 50 form elements should not slow down your page. Do some perf analysis and see who's updating your data and at what interval - id there any $digest in a loop, do you see "max 10 digest cycles reached"?
Related
I'm having some difficulty envisioning a potential solution to a dilemma I'm facing, and I need some creative inspiration.
Essentially, I'm struggling to picture a good way to validate a form that can be thoughts of as having multiple nested forms. The challenge is that these nested forms are only rendered when a line item in the main form is clicked, causing a modal to open, at which time the rendering, model binding, etc. takes place, and the nested form can be validated.
The goal is to know whether or not there are validation errors down inside any of the main form's line items without having to open/render a modal for the item to find out. I'd also like to make sure that there's no duplication of validation logic, and that things are drawing from a single common set of validations rules that can be shared/accessed everywhere needed.
Ideally, I'd like to abstract out the validation logic such that it can be used by any ng-model bound element, but can also be used independent of rendering a form.
If anyone knows of any plug-ins that work well with AngularJs and sound well suited, please let me know.
Clarification
Though I'm open to checking out any plug-ins that might help, that's not really what I'm after. My main objective to is to find a way to validate my nested item data without opening/rendering the item's modal.
I would use something that ensures that the user fills in these forms in a predefined format in the first place.
I use something called inputmask in my angularJs applications.
You can use a regex to define the format you want the inputs to be in.
You can also make sure that all the fields in the modal are in the right format before letting the user close the modal(This validation logic can come from a shared or common component).
Another option would be to make the modals hidden and not removed from the DOM so that the bindings remain even when the modal is no longer visible. You can add a red asterisk or something against the line which opens the modal to indicate errors in that modal's form.
My company just bought a third party application which is based on the ext js 4.2. framework.
The software is closed source, but it is web based such that I can add a .js file to change the UI to my needs.
I want to add some controls to the rendered page. The software is showing IDs everywhere instead of text.
Example: "Issue created by: ID123". When I hover the field is get "ID123. John Doe". Ok, I am a JS ninja, so I can just add a field to the HTML DOM which will display "John Doe"in the correct spot.
I looked at the HTML code to get the correct control and see the the IDs are generated. The code I would write is prone to break with each new release of the third party software.
Now, since the is an Ext JS application I can probably solve the issue much more elegantly like adding a field not to the DOM directly but to the Ext JS container.
Question:
In Javascript I have a reference to the Ext JS app. How would I access the current view or viewmodel or model to query data and add a field?
Rough idea/Pseudo code:
var id = app.getCurrentModel.getValue("creatorID");
var name = myserver.getPersonData(id).name;
app.currentView.addLabelControl(name);
I googled a lot but all examples I found assumed that you are writing the ext js app and you are already in the controller or the view. But I only got the reference to the app.
Sorry for the newbie question :)
"App Inspector for Sencha"
For a quick glance over component hierarchy, you can use the Sencha browser plugin,
Find a certain ExtJS component programmatically
To quickly search ExtJS components or transform your findings into code, your main tool will be the browser console and the command Ext.ComponentQuery.query(xtype), e.g.
Ext.ComponentQuery.query("grid")
Ext.ComponentQuery.query("panel")
Ext.ComponentQuery.query("form")
You will then find in browser console an array of all components of that type. Select the right one, and check whether it has an id or itemId that is not auto-generated (everything like xtype-1234 is auto-generated). For form fields, the name attribute could be useful. Commands like
Ext.ComponentQuery.query("[itemId=ABC]")
Ext.ComponentQuery.query("[name=DEF]")
Ext.getCmp(id)
are far more readable and not as prone to side effects as Ext.ComponentQuery.query("panel")[12].
Most of the time, it can also be useful to think in tree structure. If you want a certain container which contains the only slider you see, trying
Ext.ComponentQuery.query("slider")
Ext.ComponentQuery.query("slider")[0].up()
could be easier than sifting through dozens or even hundreds of containers. Ways to traverse the component structure include up(xtype), down(xtype), nextSibling(xtype), previousSibling(xtype). If an xtype is provided, the next component of the corresponding xtype is selected; if it isn't provided, the next component is selected regardless of the type (e.g. direct parent, adjacent sibling).
Change anything you want.
You can extend, debug or modify any existing behaviour, including but not limited to ExtJS's own code, using a so-called override over any component, including the views or stores that make up this app. override makes a great search term for further information.
Or you can add new components to existing components, like a button to an existing form, from outside the app. For example, open sencha docs and then insert in console:
Ext.ComponentQuery.query("searchcontainer")[0].up().insert(1,{xtype:'button',text:'Test',handler:function(){Ext.Msg.alert('Test Button clicked');}});
You should then find a button on the top left, right of the Sencha logo. Click it.
Find existing controllers
For this, you have to find the name of the app namespace.
If it is e.g. MyApp, then MyApp.app.controllers.items contains the list of controllers. Controllers contain control logic, and the mapping between the components and the logic. When components are created, controllers attach their events to these new components. Many changes can and should be made in the component layer, because controller overrides are messy.
Find viewmodels
You're already done, ExtJS 4.2 does not support them.
Changing models
If you want to change models, be cautious: There is no supported function to add fields to a model. You can override the model prototype, and push more entries into the fields array. But if you have any model instances (records) already running around by that time, they are not updated and any existing warranty is voided.
That said, you find them in MyApp.model. You can e.g. get all fields of the Sencha Docs' Comment model using Docs.model.Comment.prototype.fields, or even push another field in.
I'm currently rebuilding an enterprise app in AngularJS. It has a multi-page form with thousands of fields, which are all wrapped in custom directives (for validation, formatting, and standardization purposes) based on the field type.
I've tried to optimize these directives as best as possible (one time binding, ng-if where possible, lazy-ng-if, no $watch functions, and minimal $rootScope.broadcast's), but I'm still faced with a several second delay when loading each form page (about 50-or-so fields each) with ui-router. Looking at all my performance measurements, it seems like the delay is coming from angular compiling and rendering all these directives.
So my question is, how do I make these directives render faster? Is there a way I can compile/render the top section of my form and make it usable before the rest loads? Right now with ui-router, it seems like nothing appears until everything on the form is rendered/compiled.
I have a challenging requirement. The requirement is as follows.
We are designing a UI today with 10 UI elements.
Users are creating few records.
After a month, a new release comes and it has 12 UI elements on screen.
When the user creates new records, he should see 12 UI elements, when he opens an old record he should see 10 elements.
In order to achieve this, I thought, we could store the version with which the record was created and render the HTML belonging to that version. But, this will unnecessarily keep increasing the project size, as if the code gets 100 revisions, that much of HTML duplicates will be present.
I thought of using "switch-case" or "if-else" statements in front end, but it will slow the UI as too much heavy lifting is done by JS.
Please suggest a way to do this requirement with JS or jQuery or Angular. Any one framework is also fine, until it will work.
Thanks a lot for your responses in advance,
Look at a framework called angular-schema-forms. You write no HTML with this framework. Instead you pass it a JSON object that has the fields you want to display and their corresponding display as attributes. It comes preloaded with its own templates for all common controls and also allows you to write your own.
I am researching ways on how to dynamically add form fields for nested models and stumbled accross the nested_form plugin by ryanb. No doubt this is a a great piece of code, but I wondered why does it have to be so sophisticated?
Example: A form for creating / adding a project has one or more tasks assigned. The user can dynamically add more tasks by clicking on a add-task button. A project must have at least one task. Each task has a name and a description.
So why not just:
- When generating the html, sourround each set of task fields with a div given an ID such as "dynamic_fields"
- When the user clicks the add-task button, call a JavaScript function via link_to_function to clone the dynamic_fields subtree. Insert the new set of fields at the bottom of the task list.
- Via JavaScript, remove the values of the newly added fields and replace the child ID with something unique (Ryan suggests using a value based on the current time)
I am aware that the nested_forms plugin also works for deeper nesting structures, but given my simple use case with only one level of hierarchy, is the approach outlined above practical? Or am I missing something important? Any guidance on this topic is appreciated.
Basically, the plugin works as you describe but a form partial is used as the basis.
The ids of nested objects must be unique and it's really easy to stick to the current millisecond time to do that.
Your way to handle the problem would work but would require some additional html to catch the required parts of the form and match what belongs to which additionnal object.
Ryan Bate's code seems complicated but it's not. It introduces complex methods only to make your view look good.
I think there is another solution to this question, a gem named cocoon.