I have a view:
App.PhotoUploadView = Ember.View.extend({
images: [],
didInsertElement: function() {
var that = this;
var product = this.get('controller').get('model');
var upimages = product.get('upimages');
//this.set('images', []);
upimages.then(function(images) {
images.forEach(function(image, indexI) {
var imageObject = new Object();
imageObject.link = App.appConf.apiPaths.images + image.get('link');
that.get('images').pushObject(imageObject);
});
console.log(that.get('images'))
});
}
});
So what I'm doing here is define a images array initially empty, and fill it with objects obtained by manipulating a little the model's children...
{{#each product in model}}
{{view App.PhotoUploadView}}
{{/each}}
In the App I have many PhotoUploadView inserted at the same time; The thing is that instead of having a different images array for every instance of the PhotoUploadView, I get that every instance has a images array that contains all the images, like if the array is shared between instances;
If I remove comment to this.set('images', []); in the didInsertElement function, then everything works; so the question is: the images array is shared between the PhotoUploadView instances? Or am I missing something...?
Ember sees that as a static property and it applies to all instances of that view. If you set it as undefined up front, then define it on init (or whenever it's needed) it should fix the issue.
App.PhotoUploadView = Ember.View.extend({
init: function(){
this._super();
this.set('images', []);
}
images: undefined,
didInsertElement: function() {
var that = this;
var product = this.get('controller').get('model');
var upimages = product.get('upimages');
//this.set('images', []);
upimages.then(function(images) {
images.forEach(function(image, indexI) {
var imageObject = new Object();
imageObject.link = App.appConf.apiPaths.images + image.get('link');
that.get('images').pushObject(imageObject);
});
console.log(that.get('images'))
});
}
});
Related
I've always read that Ember works just fine with POJOs in place of Ember Data, but now that I'm giving it a shot, I'm having a little trouble.
I'm creating an app using NW.js and LinvoDB for the database. Fetching from the DB is easy and works great:
// route/index
import Ember from 'ember';
export default Ember.Route.extend({
model: function() {
var gui = require('nw.gui');
var linvoDB = require('linvodb3');
linvoDB.defaults.store = {db: require('medeadown')};
linvoDB.dbPath = gui.App.dataPath;
var File = new linvoDB('file', {} );
var Tags = new linvoDB('tag', {});
var media = new Promise(function(resolve, reject) {
var query = File.find().sort({ctime: -1}).live();
File.on('liveQueryUpdate', function() {
resolve(query.res);
});
});
var tags = new Promise(function(resolve, reject) {
var query = Tags.find().sort({name: 1}).live();
Tags.on('liveQueryUpdate', function() {
resolve(query.res);
});
});
return Ember.RSVP.hash({
media: media,
tags: tags
});
}
});
I have a simple event set up to create a tag, save it and push it into the model:
//controllers/index
actions: {
viewMedia: function(media) {
this.transitionToRoute('media', media)
},
addTag: function() {
var linvoDB = require('linvodb3');
var gui = require('nw.gui');
linvoDB.defaults.store = {db: require('medeadown')};
linvoDB.dbPath = gui.App.dataPath;
var Tag = new linvoDB('tag', {});
var tag = new Tag();
tag.name = this.get('tagName');
tag.save();
this.get('model.tags').push(tag);
}
}
I can verify that the tag object is being inserted correctly in the the tag array in the model, but the view isn't updating. From what I've read, that's cause I'm not using Ember.Object.
How are you suppose to do this with POJOs or do I have to use Ember.Objects? Thanks.
Ember provides it's own array implementation in Ember.Array/Ember.MutableArray that adds a lot of nifty things, like being properly observable in the Ember ecosystem. The Ember.MutableArray in particular (or rather Ember.MutableEnumerable if you want to go deep) has a method called pushObject(obj) that Push the object onto the end of the array. and also notifies any subscribers.
Since Ember also is nice enough to add these to the regular Arrays prototype to make it easy for people to get going, you should be able to simply do this.get('model.tags').pushObject(tag); in your code.
I have a custom component that expects data and not a promise, but I am unsure if they way that I am obtaining the data is the right way.
Is this the right way to do it?
component hbs
{{x-dropdown content=salutations valuePath="id" labelPath="description" action="selectSalutation"}}
Doesn't work
controller (this is the way I expect things to work
import Ember from 'ember';
export default Ember.Controller.extend({
bindSalutations: function() {
var self = this;
this.store.find('salutation').then(function(data) {
self.set('salutations', data);
});
}.on('init'),
components/x-dropdown.js
import Ember from 'ember';
export default Ember.Component.extend({
list: function() {
var content = this.get('content');
var valuePath = this.get('valuePath');
var labelPath = this.get('labelPath');
return content.map(function(item) {
return {
key: item[labelPath],
value: item[valuePath],
};
});
}.property('content'),
This works
controller
bindSalutations: function() {
var self = this;
this.store.find('salutation').then(function(data) {
self.set('salutations', data.get('content')); // pass the content instead of just the data
});
}.on('init'),
component
...
list: function() {
var content = this.get('content');
var valuePath = this.get('valuePath');
var labelPath = this.get('labelPath');
return content.map(function(item) {
return {
key: item._data[labelPath], // access through the _data attribute
value: item._data[valuePath],
};
});
}.property('content'),
Ember Data returns a Proxy Promise. This means you can use the promise as if it were a collection or model itself, as long as you aren't dependent on the property being completely populated when you use it. If you really want the promise resolved, you should probably be setting it up in the route.
If you want it on your controller, you can be lazy and do it like so:
Controller
salutations: function() {
this.store.find('salutation');
}.property(),
Component
...
list: function() {
var content = this.get('content'),
valuePath = this.get('valuePath'),
labelPath = this.get('labelPath');
return content.map(function(item) {
return {
key: item.get(labelPath),
value: item.get(valuePath),
};
});
}.property('content.[]'),
Template
{{x-dropdown content=salutations valuePath="id" labelPath="description" action="selectSalutation"}}
The real trick is to watch if the collection is changing. Hence you'll see I changed the property argument to content.[]
I have a controller that observes a search field like so:
Scrolls.IndexController = Ember.ArrayController.extend({
searchResult: function() {
var that = this;
this.get('model').set('content', this.store.filter('scroll', function(item) {
var searchTerm = that.get('searchCard');
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
}));
}.observes('searchCard')
});
Which works great, but once I add a method that overrides arrangedContent to limit the returned items, it stops re-rendering.
Scrolls.IndexController = Ember.ArrayController.extend({
arrangedContent: Ember.computed('content', function() {
var count = 0;
return this.get('content').filter(function() {
count++;
return count <= 3;
});
}),
searchResult: function() {
var that = this;
this.get('model').set('content', this.store.filter('scroll', function(item) {
var searchTerm = that.get('searchCard');
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
}));
}.observes('searchCard')
});
How can I get make what I'm doing to behave nicely with each other?
I see a few things here that jump out to me. First one being, in the context of a controller, content and model are the same thing so in the observer, when you do:
this.get('model').set('content'
You're setting a property of 'content' on the model when I think you actually intend to set the content directly on the controller, like this:
this.set('content',
I also kind of wonder whether you really need to override the content and arrangedContent properties (not sure what the calling code looks like). I suspect that might cause some bugs later. Instead, I wonder if you could set it up like this:
Scrolls.IndexController = Ember.ArrayController.extend({
firstThreeSearchResults: function() {
var count = 0;
return this.get('searchResults').filter(function() {
count++;
return count <= 3;
});
}.property('searchResults'),
searchResults: function() {
var searchTerm = this.get('searchCard');
return this.store.filter('scroll', function(item) {
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
});
}.property('searchCard')
});
Final possible problem is the use of the filter function called on the store. According to the docs, this function: "returns a live RecordArray that remains up to date as new records are loaded into the store or created locally." The problem being, though the filter might update as new results are added, it might not cause the computed property that looks for the first three results to update. That is, the binding on that computed property might not fire. One way to get around this would be to do something like this:
Scrolls.IndexRoute = Ember.Route.extend({
model: function() {
return this.store.find();
}
});
Scrolls.IndexController = Ember.ArrayController.extend({
firstThreeSearchResults: function() {
var count = 0;
return this.get('searchResults').filter(function() {
count++;
return count <= 3;
});
}.property('searchResults'),
searchResults: function() {
var searchTerm = this.get('searchCard');
return this.get('content').filter(function(item) {
var regExp = new RegExp(searchTerm, 'i');
return regExp.test(item.get('name'));
});
}.property('searchCard', 'content.length')
});
I feel like this should be pretty straight-forward, but I'm unable to get the contents of a controller in a different view. Here is my code:
App.MapView = Ember.View.extend({
elementId: ['map-canvas'],
didInsertElement: function() {
var self = this;
var controller = this.get('controllers.markers');
}
});
If I console.log(controller) I get undefined.
In a controller I would do something like:
App.MarkersController = Ember.ArrayController.extend({
needs: ['map']
});
App.MapController = Ember.ObjectController.extend({
plot: function() {
var markers = this.get('controllers.markers');
}
});
You place the needs on the controller that needs another controller, and where you'll be accessing the other controller.
And from a view, in order to grab the controller you do this.get('controller') and the controllers object lives on the controller, so controller.controllers.markers
Additionally, the view is only created with the controller by default if ember creates it, if you are doing something like {{view App.MapView}} it isn't creating the MapController and associating it with it, it's using the controller that was in scope when you created the view.
App.MapView = Ember.View.extend({
elementId: ['map-canvas'],
didInsertElement: function() {
var self = this;
var controller = this.get('controller.controllers.markers');
}
});
App.MarkersController = Ember.ArrayController.extend({
});
App.MapController = Ember.ObjectController.extend({
needs: ['markers'],
plot: function() {
var markers = this.get('controllers.markers');
}
});
Check out this implementation of it:
http://emberjs.jsbin.com/ODuZibod/1/edit
For example if I have a Person model with the "name" attribute, what can I call in Ember.js with Ember Data that returns an array of all the names in the Person model?
App.Person.find().then( function(data) {
var namesArray = data.getEach('name');
});
UPDATE RE: COMMENT (what if i want to do this from setupController...)
setupController: function(controller, model) {
App.Person.find().then( function(data) {
controller.set('variablename', data.getEach('name') };
});
}
App.PersonsRoute = Ember.Route.find({
setupController: function() {
// Get all persons
this.controllerFor('persons').set('content', App.Person.find());
}
});
App.PersonsController = Ember.ArrayController.extend({
allNames: function() {
var persons = this.get('content') || [];
return persons.getEach('name');
}.property('content.[]')
});
In short, when you have a collection (an array of objects) and you want to build a new array of the values of a certain property, use getEach. getEach('foo') is an alias for mapProperty('foo').