add event on input angular 2 dynamic form - javascript

I have a form create with dynamic form following the tutorial in the cookbook now I want catch when some input change so how can add (change) events, send the function to call as param; something like
new TextboxQuestion({
key: 'test',
label: 'Test ',
type: 'text',
onChange: 'test()',
order: 0
})
thanks

Since you're creating this form dynamically, you should therefore have access to it in your controller. Angular 2 uses the concept of the FormControl which is a very powerful tool for interacting with your form controls. I'll skip a lot of the details, but I'll point out a couple of things:
You've got access to FormControl.valueChanges, which might not be apparent from that doc because FormControl inherits from AbstractControl, a superclass that provides a huge chunk of useful functionality, much of which is very similar to the Angular 1 forms functionality.
Essentially, what you're looking to do is leverage observables. If you're unfamiliar with them, you should read about them. Christoph Burgdorf has a great article on them and actually, conveniently for you, uses FormControl.valueChanges as part of his example on how to use them. Oh what a lovely day it is!
But basically, in a nutshell, you're looking to do something along these lines:
this.textboxControl.valueChanges.subscribe(value => {
//... do your stuff here with 'value'
});
This obviously assumes that you've got an instance of the form control that you're wanting to react to.
IT IS EXTREMELY IMPORTANT that when using observables you UNSUBSCRIBE in the ngOnDestroy method in your controller; otherwise the subscription will hang around after you're done with it and you have a memory leak.

Related

Ember Data - Lazy loading child data of a model without re-creating previously created objects again

I'm new to ember-data. I'm trying to load comment list from a API using multiple API calls. The comment list feature works like below,
A comment object can have a parent comment or children comments (replies)
All comments (children & parent) from different comment threads are list down in a single comment list using a 1st API call.
If user click on specific comment from above list it will prompt respective comment thread. Respective parent or children comments loading using 2nd API call
The comment model is implemented as below,
export default CommentModel.extend( {
parent: computed(function() {
return get(this, 'store').queryRecord('comment', {
_overrideURL: `comments/${get(this, 'id')}/parent`,
});
}),
children: computed(function() {
return get(this, 'store').query('comment', {
_overrideURL: `comments/${get(this, 'id')}/children`,
});
}),
...
As this implementation, if user click on child comment (reply) from the comment list, the 2nd API call with load the respective parent comment and parent comment will load its children comments again. That behaviour cause reload the comment list component in UI.
Is there any other way in ember-data to lazy load relationship without creating already existing objects?
If you really need to go that road, you may try to perform a findRecord instead of a queryRecord and use adapterOptions to customize your model's adapter urlForFindRecord method.
TL;DR
Why you shouldn't:
IMHO, you have a data flow problem in your proposed design.
You shouldn't be performing async code inside a computed property (nor returning immutable object as queryRecord response).
Tasks work great for that purpose.
You shouldn't be having your model to load data (that should be route's responsibility), which violates both MVC and DDAU principles.
There is this great article from 2015 on that
As a matter of fact since ember octane, you shouldn't be using computed properties at all, they have been replaced by actual getters and tracked properties.
More on that
Ember is a great framework, good luck on your journey!

Using Redux with vanilla JS how can I reach it without passing it to every component?

I'm using Redux in a vanilla JS project. I have a bunch of small modular UI files and controllers and such. In those UI files I might have code like:
const ExampleForm = function (StoreInstance) {
return $('<form />', {
submit: () => {
StoreInstance.dispatch({
type: 'EXAMPLE_DISPATCH',
post: {
message: $TextareaComponent.val()
}
})
return false
}
})
}
The issue is I have a lot of simple view files like this and many of them are nested and I'm finding it to be ugly and error prone to have the store passed as a param to everything.
For example, I trimmed it for brevity but the form component has form element components such as a textarea. Currently I see two options of managing the Store:
Setting it to window when creating it in my entry file (index.js) and then just accessing Store globally. This seems the nicest, although not "best practice" and makes unit testing and server side rendering a bit harder.
Passing it to every component tediously. This is my example above. This I'd consider as "best practice" but it's pretty annoying to do for every file you make almost.
I'm wondering if there's any alternatives or tricks to passing the store instance. I'm leaning towards just making it global.
You could use the constructor pattern and create every view as new ConnectedView(). The ConnectedView would have a memoized instance of the store (this.store within the view), so it doesn't need to be global.

Proper way to bind to data object in Angular 2 service?

I am building an angular 2 application. The documentation has changed quite a bit since the released which has caused confusion. The best I can do is explain what I am trying to do (Which was easy in Angular 1) and hope someone can help me out.
I have created a login service using JWT's.
Once login is successful, I return a user object.
I have a loginComponent ( binds data to template ) and loginService ( which handles the https calls )
I have a userService which maintains the user object.
I have a userComponent which renders the user data.
The problem is, once the user has logged in, I am unclear on the best approach for letting the userService retrieve the new data in an object called "user", then the userComponent update its user object on the template. This was easy in angular 1 simply by putting a watcher on the userService.user object.
I tried Inputs and Outputs to no avail, eventEmitters, Observables and getters and setters. The getters and setters work, but force me to store everything in a "val()"
Can someone please tell me the best way to achieve this?
User Component renders template with user.firstName, user.lastName etc.
Initially user if an empty Object
The login service needs to set the UserService.user
The userComponent Needs to detect the change and update the DOM.
Thanks in ADVANCE!
If I'm not wrong, you are looking for a way to 'listen' to changes in your UserService.user to make appropriate updates in your UserComponent. It is fairly easy to do that with Subject (or BehaviorSubject).
-In your UserService, declare a property user with type Subject<User>.
user: Subject<User> = new Subject();
-Expose it to outside as observable:
user$: Observable<User>
...
this.user$ = this.user.asObservable();
-Login function will update the private user Subject.
login(userName: string, password: string) {
//...
this.user.next(new User("First name", "Last name"));
}
-In your UserComponent, subscribe to UserServive's user$ observable to update view.
this.userService.user$.subscribe((userData) => {this.user = userData;});
-In your view, simply use string interpolation:
{{user?.firstName}} {{user?.lastName}}
Here is the working plunker: http://plnkr.co/edit/qUR0spZL9hgZkBe8PHw4?p=preview
There are two rather different approaches you could take:
1. Share data via JavaScript reference types
If you create an object in your UserService
#Injectable()
export class UserService {
public user = new User();
you can then share that object just by virtue of it being a JavaScript reference type. Any other service or component that injects the UserService will have access to that user object. As long as you only modify the original object (i.e., you don't assign a new object) in your service,
updateUser(user:User) {
this.user.firstName = user.firstName;
this.user.lastName = user.lastName;
}
all of your views will automatically update and show the new data after it is changed (because of the way Angular change detection works). There is no need for any Angular 1-like watchers.
Here's an example plunker.
In the plunker, instead of a shared user object, it has a shared data object. There is a change data button that you can click that will call a changeData() method on the service. You can see that the AppComponent's view automatically updates when the service changes its data property. You don't have to write any code to make this work -- no getter, setter, Input, Output/EventEmitter, or Observable is required.
The view update automatically happens because (by default) Angular change detection checks all of the template bindings (like {{data.prop1}}) each time a monkey-patched asynchronous event fires (such as a button click).
2. "Push" data using RxJS
#HarryNinh covered this pretty well in his answer. See also Cookbook topic Parent and children communicate via a service. It shows how to use a Subject to facilitate communications "within a family".
I would suggest using a BehaviorSubject instead of a Subject because a BehaviorSubject has the notion of "the current value", which is likely applicable here. Consider, if you use routing and (based on some user action) you move to a new route and create a new component, you might want that new component to be able check the "current value" of the user. You'll need a BehaviorSubject to make that work. If you use a regular Subject, the new component will have no way to retrieve the current value, since subscribers to a Subject can only get newly emitted values.
So, should we use approach 1. or 2.? As usual, "it depends". Approach 1. is a lot less code, and you don't need to understand RxJS (but you do need to understand JavaScript reference types). Approach 2. is all the rage these days.
Approach 2. could also be more efficient than 1., but because Angular's default change detection strategy is to "check all components", you would need to use the OnPush change detection strategy and markForCheck() (I'm not going to get into how to use those here) to make it more efficient than approach 1.

Private variables in Ember-data DS.Model

I want to store a private variable on each DS.Model. Its purpose is to store a pending callback (in case I want to cancel it).
I have tried this (and it works):
DS.Model.reopen({
init() {
let _pending; // my private var
this._getPending = () => _pending; // get private var
this._setPending = callback => _pending = callback; // set private var
this._super(...arguments);
}
});
I have placed this in an initializer, and it works as I expect it to.
My questions are: Is this a good practise? is it likely to mess anything up? ...and, is there a better way?
Personally, I'm happy with the way it works.. but I'm not sure if its the "Ember" way. This is going to go into an Ember-cli addon, so I would like it to be the most "best practise" as possible. (the _getPending/_setPending method are only to be used internally within the addon.)
Here are my 2 cents on this. I would say no it is not a good practice, but it should be okay since they are just Ember Objects. The question here is what is Ember data model used for? From doc it says:
"Models are objects that represent the underlying data that your application presents to the user."
By definition this is not what they are designed for, so just because you are able to it does not mean that you should use them like this.
Pending callback so it can be canceled? Ember model API has defined state objects that can be used for this purpose. http://emberjs.com/api/data/classes/DS.Model.html Flags like isDeleted, isValid, isNew...gives all possible state.
I would place them in router actions where they are easy tested with integration tests.
You can check this screencast that explains them:
https://www.emberscreencasts.com/posts/102-ember-data-20-model-states-and-flags
Hope it helps.

What's the secret to data-binding?

For most JS frameworks and libraries, the value they bring are often in the form of a new structure as to how to build an application (Backbone, React), or new ideas that effectively power-up the language (Angular), or simply the methods they offer are well tested, fast, and really convenient (jQuery).
Usually the ideas and methods they provide are pretty straightforward usage of JavaScript, but with a very clever team behind it that find interesting ways to do things which you can think through and get a solid guess as to how the guts work.
However, I've been unable to think through the ability to two-way bind JS models to view components. What is the secret sauce at the heart of this feature that makes this work? Changing an internal variable from a user input is simple, but what about the reverse? How would you be able to "know" when a JS variable has changed in order to update the display instantly? Surely it can't be polling, so what then?
Whenever a block of your JS runs that angular triggered it will run a digest cycle when the block finishes executing. This basically checks all the values that might of changed and would require updates to the view.
If angular didn't trigger the code then it won't know that something might of changed so your bindings can get out of sync. For example if you run something like this
setTimeout(function() {$scope.myValue = '123'});
Angular won't know that myValue changed and it actually won't update the view. That's why Angular has it's own services for doing everything. e.g. $timeout or $http.
If you have some callback function that Angular doesn't know about then you can manually tell it to check for changes by calling $scope.$apply()
there are several ways to do it. Object.observe is great, but lacks good support. You can poll for values as well, keeping a 2nd copy of the object around to compare. You can also write your own explicit set/get methods to update the model like backbone does.
One neat method i use a lot is using getters/setters to keep the model synced to the dom:
//a demo "model" of data:
model = {
name: "Fred"
};
function change(k,v){alert([k,v]);} // a stand-in change monitor for demo
// iterate model and replace values with getter/setter combos:
Object.keys(model).forEach(function(key) {
var val = model[key];
delete model[key];
Object.defineProperty(model, key, {
get: function() {
return val;
},
set: function(v) {
val = v;
change(key, val);
} //call change upon setting
});
change(key, val); //update view "onload"
}); // alerts "Fred";
//update model (fires change() with "name" and "sally" arguments:
model.name="sally"; // alerts "sally";
the change function is quite simple and for your case should just find elements bound to keys.
the advantage here is that you don't need special custom CRUD methods, you can just modify the object properties via assignment like it's 1999. It also doesn't poll, and works correctly all the way back to IE9 and any other ES5 environments. It's the simplest way to bind JS>DOM (afaik) without custom methods.
It does have some limits: nested objects are tricky to get/set upon, you can't do the whole object at once, you can only "watch" primitives. Arrays are a problem too: you can't really replace expando properties with getters/setters without side-effects. But, upon a relatively flat collection of JSON-safe data, get/set works a charm and needs no complex libs to get operational.
checkout a complete example using this method: http://pagedemos.com/xg3szbguqnwu/4
I can speak to how it's done in Backbone, which has a relatively low-level perspective on data-binding.
It's a combination of 1. the library having control over attribute setter methods 2. invoking callback functions when attributes change (e.g. by dispatching events) in order to update the UI.
The essential pseudocode is this:
class Model:
method set(name, value):
if value != this.attributes[name]
this.triggerEvent('change', name, value)
this.attributes[name] = value
m = new Model()
someInputWidget.onEvent('userChangedInput', function(value) {
m.set(someInputWidget.name, value)
})
m.onEvent('change', function(name, value) {
getInputWidgetByName(name).setValue(value)
})
Backbone does not do any data binding to the UI, but you can refer to Backbone's annotated source for the actual event-dispatching implementation.

Categories

Resources