In my meteor app, I am using iron-router's waitOn method to subscribe to all relevant documents and the data method to return just one document's contents to the template as a reactive variable. (Since these documents are large, I want to load just one at a time as a reactive variable for speed concerns).
//...
waitOn: function() {
// subscribe to ohlcOneMin, ohlcThreeMin, etc.
return Meteor.subscribe(this.params.exchange, this.params.market);
},
data: function() {
return {
ohlc: Widgets_Exchanges_Btcchina.findOne({market: this.params.market, dataType:'ohlcOneMin'})
};
},
//...
By default on route load, iron-router will store data from the ohlcOneMin document under the Router.current().data().ohlc reactive variable. And that works great in my template. Now, imagine I have a button in my template. On click, it would change the iron-router data source to the contents of the ohlcThreeMin document... essentially toggling iron-router's Router.current().data() reactive variable between different document data which the user is subscribed to, without having to re-load the route or have all the data from all the documents stored in a reactive variable at the same time. How can this be achieved? Thanks!
Related
I am working on a Backbone/Marionette project. This project implements a way to cache data on local memory after loading them from server. Therefore data can be access anytime, anywhere within the project.
This makes me wonder what is the better way to populate data to view in my case:
const ChildView = marionette.View.extend({/*...*/});
const ParentView = marionette.View.extend({
// ...
onRender() {
// 1: pass data to child view from parent view
const childView = new ChildView({
data: this.options.data,
}));
// 2: initialize data when creating new child view
const childView = new ChildView({
data: SomeModel.new({/* some properties */}),
}));
},
// ...
});
new ParentView({
data: SomeModel.new({/* some properties */}),
}).render();
Both methods work correctly. However, the project view structure is pretty deep and complicated so I prefer the second way because with the first one I would need to go up and down a lot to check what data is and where it comes from.
Do you think if there are any possible problems with this method?
I prefer the 1st way, passing data from parent to child, but it depends on what your views are doing.
For me, a big advantage of sharing a data object is that updating it within one view updates it in all other views (this will work if you pass an existing backbone Model, or any object as data). This can save a lot of work... when a user updates their background color (for example), you can update it once in your BackgroundColorChoose view, and know that it is already updated everywhere else that data is in use.
In a sense, it doesn't matter where the data came from, only what it represents (because it can be accessed/modified from within any of your views).
I can imagine scenarios where this approach is not good, but I've found it makes a good baseline to start from (and avoids the need to trust browser-caching)
Inside an application we allow users to create new records, related to an existing record. To achieve this, we use actions something like this:
createUser() {
var route = this;
var model = this.store.createRecord('user', {
client: route.modelFor('client'),
});
route.transitionTo('user.update', model);
},
The user.update route renders a user-form component, using the model that was passed in the transition. The same route is also used to update existing users.
The issue with this approach is as follows; when refreshing the page, the page errors because the route fails to find the respective record when querying the store (at this point, the URL is /users/null/update). Ideally I'd pass the client (or client.id) argument in the URL so that:
The page can be reloaded without issue.
The client associated with the user is set correctly.
How can I achieve this in Ember.js? I know that this can easily be done using nested routes (by nesting the user.update route inside a client route), but this doesn't make sense visually.
The relevant parts of the router are as follows:
this.route('clients');
this.route('client', {path: 'clients/:id'}, function() {
this.route('users');
});
this.route('user', {path: 'users/:id'}, function() {
this.route('update');
});
All I do in the user/update.hbs template is {{user-form user=model}}
The problem is that the model you just created has no id at that point because it is not saved, ember can´t route to a model without an id, if possible save the model before you try to transition to the route, if you don´t want to save the model because the user can cancel the action check this thread where a user had the same problem (if I understand you problem correctly), I provided a solution for that problem that I´m using in my own project
https://stackoverflow.com/a/33107273/2214998
Just for example:
I have directive inside ngView with this structure:
.directive('features', function() {
templateUrl: '.../',
link: function(scope) {
// HTTP req, getting remote data.
// Store the remote data to the scope
}
})
When I change the route and return it back, the directive link option is executed again, the scope is empty. It need to wait some time for data-response from the remote server and then showing the data. I trying to avoid the layout stretching.
I am new to angular, I was read the documentation.
My question is: In this case, where the data is good to be saved? Do I need to make a controller? Or I can just cache it?
Note: This directive repeating an array (remote data) and making "features" HTML layout. This directive will be used in another routes with another behaviors.
I do not need code explanations, I can read docs. I accept terminology.
You could store the data in a service, basically to act as a cache as you mentioned. If you need to have that data available on page reloads (hard refresh, your app is reloaded again) you can store it in a cookie/local storage.
app.service("featuresService",function($http){
var cachedFeatures=null;
return{
getFeatures:function(callback){
if(!cachedFeatures){
$http.get("...").success(function(data){
cachedFeatures=data;
callback(data);
})
}else{
callback(cachedFeatures);
}
}
}
})
I know the sendAction will send actions from a component to the controller associated with the template where it has been placed, but in my case the component is not placed directly inside a route's template. Instead, my component is inside a view's template:
<script type="text/x-handlebars" data-template-name="displayTemplate">
<h3>View</h3>
...
{{componentA}}
</script>
This component has a controller associated with it:
App.ComponentAController = Ember.Controller.extend({
...
}
But the wrapping view does not, it's just a view:
App.DisplayView = Ember.View.extend({
templateName: 'displayTemplate',
actions: {
updateData: function(data) {
/* how can I get this triggered when an action happens in the embedded componentA? I'd like to
process the data object here so I can update the wrapping view accordingly. */
}
}
...
}
If I implement the action in the component's controller, it is not triggered when I perform a sendAction('updateData', data) from within the component. If I implement that in DisplayView, it is not triggered either.
So the question is: when an action is triggered in componentA, how can I send that action to the view DisplayView for handling?
More specifically, I'm trying to send context data to that view so it can be updated depending on what is selected on the embedded component. So if there is another way of communicating this data that'd work too. I chose actions just because it seemed appropriate, but maybe it is not.
UPDATE: The actual scenario
The actual code refers to a grid view, which in it's template has a pagination component. When the user switches to a new page, the pagination component sends a request to the server for the selected page.
Once data is returned, it then needs to let the wrapping view (which contains the grid) know that new data is available. When the user clicks on the page number, the action is handled in the pagination component and that's why I make the data call from there. If I needed to paginate something else, I wanted to reuse this component so I didn't want to make the pagination part of the grid view.
Normally you pass target, but the context of a component the property name is targetObject.
{{foo-bar action='actionName' targetObject=view}}
I know how to conditionally publish data in meteor, but how do you conditionally subscribe?
I'm thinking of the case where you have an application with separate pages (using meteor-router, for example) and you only need certain subscriptions on certain pages.
Use if statements in a Deps.autorun() method. The if statements should test against Session variables, which will make it reactive, triggering Deps.autorun to change the subscriptions when the conditions change. Thus:
Deps.autorun(function() {
sub.stop(); // Stop the previous subscription, because it's changed.
if (Session.equals('page', 'foo')) {
sub = Meteor.subscribe('bar');
} else if (Session.equals('page', 'lorem')) {
sub = Meteor.subscribe('ipsum');
}
// etc.
});
You could also do a multi collection publish/subscription depending on the page. As the page changes the subscription is automatically changed depending on what the page is:
client side js
Deps.autorun(function() {
Meteor.subscribe("page_subscriptions", Meteor.Router.page());
});
server side js
Meteor.publish("page_subscriptions", function(page) {
if(page=="home") {
return [
Collection1.find({}),
Collection2.find({}),
Collection3.find({})
];
}else if(page=="about") {
return [
Collection3.find({}),
Collection4.find({})
];
}
});
So each page has its own set of collections which can have individualized queries, while using the same subscription.
Because Meteor.Router.page() is reactive, as soon as you route to a different page the subscription will be changed.