Remove data binding in angular2 - javascript

Angular 2 data binding is great but i can't seem to find a angular 2 way of removing data binding on specific variables. My reason for this is i started hooking my application up to indexed DB and it works but i can't allow the temporary cache (just an array of all the indexed DB values) to be subject to data binding (if it was then the temporary cache would no longer mirror the database) my database is on an angular2 service. now i have found a way of removing the data binding but it isn't exactly pretty my code is this
app.copy=function(item){
return JSON.parse(JSON.stringify(item,app.replacer),app.reviver);
}
app.reviver=function(key,value){
if(value.fn){
value=new Function(value.parameters,value.body);
}else if(key==="time"){
value= new Date(value);
}
return value;
};
app.replacer=function(key,value){
if(typeof value ==="function"){
value=value.toString();
value={
fn:true,
parameters:value.match(/\(([\s\S]*?)\)/)[1].replace(/[\s\r\/\*]/g,""),
body:value.match(/\{([\s\S]*)\}/)[1].replace(/[\t\r\n]/g,"")
};
}
return value;
};
like i said it works but it isn't pretty. i can just run app.copy on the variables before they leave the cache so that they don't get data bound to anything. I was wondering if there was a cleaner way to tell angular 2 this variable isn't suppose to be data bound. and if not then at least i was able to get my solution up here for others.

If you establish "binding" imperatively you can stop the binding imperatively. There is currently no support in Angular2 to cancel a declarative binding imperatively.
Bind the view only to fields of the component.
Use observables in the service that fire an event when values change.
In the component subscribe to the observable and update the fields in the component when values in the service change.
Update values in the service when values change in the component.

Related

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.

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.

D3 and two-way data binding

I am interested in the current best practices and solutions for using the data driven documents library with two-way AJAX data bindings. More specifically I am wondering how d3 should be best integrated with libs supporting two-way data bindings such as Angular or Knockout.
The obvious conflicts that arise stem from the fact that d3 and the AJAX libs are both inserting data to the DOM, which basically means that one has to wrap the other.
About Data on DOM
You were worried about the data inserted to the DOM. This are some of the properties added:
D3js: __data__, __onmouseover.force, __onmouseout.force, __onmousedown.drag, __ontouchstart.drag, __onmousedown
AngularJS: value, type, version, align, ng339
So there's no colisions and no need to wrap one into another. You can test this using Object.keys(SOME_DOM_ELEMENT); and Object.keys(SOME_DOM_ELEMENT.__proto__);
About Implementation
Pure javascript
This is how you assign data to D3js:
d3selector.data( myNameSpace.myDataObject );
And this is my data binding approach using watch: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/watch
d3selector
myNameSpace.watch('myDataObject', function (id, oldval, newval) {
d3selector.data( newval );
return newval;
});
This way, everytime you change myNameSpace.myDataObject the data used by D3js will be updated too. But this will only work on Firefox.
AngularJS
There's an answer in this question Angular + d3.js: Data binding with SVG text attr? that explains how to do it using $watch.
Is similar to the Firefox watch:
directive('myDirective', function ( /* dependencies */ ) {
// Imagine your $scope has myDataObject
$scope.$watch('myDataObject', function (newVal, oldVal) {
d3selector.data(newVal);
});
}
Now everytime you change myDataObject in the $scope the data of D3js will be updated too.
More info
Here is an example of two way data binding using polymer: http://bl.ocks.org/psealock/a4f1e24535f0353d91ea you can test it here: http://bl.ocks.org/psealock/raw/a4f1e24535f0353d91ea/
As you can see in refreshChart the binding is not really being used. Instead, on the event triggered when the data changes, D3js loads the new data:
this.g.data(this.pie(this.data));
D3js is not prepared to listen for changes on the data, unless you use the data method. That's why the already rendered data will not change.
If in the future data bindings were implemented, I guess there will be a new method on selection:
selection.update - return placeholders for updated elements.
similar to the current enter and exit:
selection.enter - returns placeholders for missing elements.
selection.exit - returns elements that are no longer needed.
removing the need to create refresh functions.

knockout.js preventing first value change

I've a view with knockout.js which has some textboxes and dropdowns.
known when the user changes a value i save the data with a $post
for this i created some computed propties like
self.subjectChanged ko.computed(function () {
var subject self.subject();
//save...
But this also triggers when the subject was loaded from database and set for first time.
What is the best practice for this ?
A similar problem is that i have a function getdata() which depends on two properties.
Now on load this method is raised twice (for each property)
What are best practices to handle this szenarios ?
One way of doing it is to load the page and bind the data as normal, and then use subscriptions to monitor changes to the observable you are interested in.
http://knockoutjs.com/documentation/observables.html#explicitly-subscribing-to-observables
viewModel.subject.subscribe(function(newValue) {
// code you want to run when the value changes...
});
for example http://jsfiddle.net/m8mb5/
This may not be best practice, but in the past I tied a loaded variable to the vm and when the data finished loading from the server I set it to true;
In my computeds I would surround the code that actually did the work in an if that checked the loaded. Computeds can be a little tricky though, you may need to reference the observables outside of the if to ensure they fire correctly.
com = ko.computed(function(){
if(loaded){
var subject = self.subject();
}
// reference observable outside of if to ensure the computed fires when the observable changes
self.subject();
});

rivets.js: prepopulate model with data from view on init

Perhaps this seems a bit backwards, but I have a view bound with Rivets.js for which I'd like the view to populate the model on initialization.
The usecase is that I'm using server-side rendering to return a snippet (the view) including rivets' data-attributes. So NO JSON is returned from server to client.
Now, by pressing 'edit' a user may put the content in 'edit'-mode, and start editing at will. (Using contenteditable, but this is out of scope here I guess).
So how to make sure the model is populated with values from the view on init?
I know that this question is a little outdated but I recentry tried rivets and I came across the same problem.
The solution:
// In your rivets configuration you disable preload:
rivets.configure({
templateDelimiters: ['[[', ']]'],
preloadData: false
});
// you bind your data
var binding = rivets.bind($('#auction'), {auction: auction});
// you manually publish it once to populate your model with form's data
binding.publish();
And that's it. I still don't know how to disable prelaod per bind
From the example on Rivets website (assign to 'rivetBinding')
var view = rivets.bind($('#auction'), {auction: auction});
doing rivetBinding.publish(); will bootstrap the model with values from the view for all bindings that have 'publishes = true'.
This question is old but it still has no accepted answer, so here goes:
You need to disable the preload configuration so rivets doesn't override whatever is in the input with what you have in your model at the time you do the binding. This can be done via the preloadData=false configuration, either globally (rivets.configure(...)) or view-scoped (third param to rivets.bind(...)).
After the binding, you need to publish the view (pull the values to your model). You also need to set up the observers via sync() call, otherwise your binded methods won't be triggered.
Using the same example as the previous answers:
var view = rivets.bind($('#auction'), { auction: auction }, {
preloadData: false
});
view.publish();
view.sync();

Categories

Resources