This is my code so far:
<Mutation mutation={addUserQuery}>
{
(addUser, data)=>{
console.log(data)
return (
<div className="form">
<form onSubmit={(e)=>{
e.preventDefault();
console.log(e);
addUser({variables: {username: "AuraDivitiae",
firstname: "Michael",
lastname: "Lee"}})
}}>
<button type="submit">Add User</button>
</form>
</div>
)
}
}
</Mutation>
What does Apollo do when a mutation component mounts?
I feel like I don't really understand the processes running inside Apollo.
Does Apollo subscribe to the result of the mutation query?
Does it then update the cache on returning?
Is Data then stored in some components state?
I feel like the documentation doesn't provide enough information sometimes...
<Mutation/> component is ... a normal react component - it has own state, lifecycles, it's using apollo client (and its cache), keeps data.
It's probably a bit confusing that being in render we have rerenderings not caused by setState of our component.
If <Mutation/> is a component then de facto your inner content is rendered by render function of <Mutation/>, not in our component (it only renders <Mutation/> component). This is an additional depth level in components tree structure (with own lifecycles).
Related
When I am making an AJAX request to update some values the data or model is changing. But the updated model is not reflecting immediately. It is reflecting only after clicking refresh. I want to only modify a view of a div in the page. I have tried many things but not successful. Can anyone suggest me the way to solve the issue?
The git repo:
https://github.com/SyamPhanindraChavva/trell-app-front-end
templates/note.hbs
<div class="dropdown-menu" aria-labelledby="dropdownMenuButton">
<p><a class="dropdown-item" {{action 'updateNote' "doing" note.id}}>DOING</a></p>
<p><a class="dropdown-item"{{action 'updateNote' "done" note.id}}>DONE</a></p>
<p><a class="dropdown-item"{{action 'deleteNote' note.id}}>DELETE</a></p>
</div>
controllers/notes.js
status2:Ember.inject.service('work-status'),
actions: {
updateNote(upstatus,id){
let status1 = Ember.get(this,'status2');
status1.makeputrequest(upstatus,id);
this.toggleProperty('parameter');
}
}
services/work-status.js
makeputrequest(status1,id1)
{
$.when($.ajax ({
// get(this,'layout.referrer.owner.router').transitionTo('notes');
type: 'PUT',
url:'http://localhost:3000/notes/'+id1,
data: {note:{status:status1}},
})).then(function(result){
console.log(result);
}).catch(function(err){
console.log("you have error")
});
},
The thing I am trying to make is whenever I change the status of a record the corresponding record in the UI must also go to the corresponding table or container in UI.
Because you're using ajax calls directly in ember, you'll be responsible for setting the new properties once it's complete.
In services/work-status.js, you'll want to return the promises generated by the ajax calls.
In controllers/notes.js, chain .then to the returned promise and call and set the new property.
EG - controllers/notes.js
updateNote(upstatus,id){
let status1 = Ember.get(this,'status2');
status1.makeputrequest(upstatus,id).then((){
this.get('model').findBy('id',id).set('status',upstatus);
});
},
With that said, this isn't the correct route to take. You're mixing ember-data with plain ajax calls which is making the logic of your codebase more difficult than necessary.
If you modify your action to take the entire note as an argument, change the status, and just save the model, you wouldn't need to use the service.
// templates/note.hbs
<p><a class="dropdown-item" {{action 'updateNote' note "doing"}}>DOING</a></p>
// controllers/note.js
updateNote(note,upstatus) {
note.set('status',upstatus)
note.save();
}
Ajax is only meant if you are not trying to change something on the records. However, if you would like to change a value of your records you should be using ember-data
I find I'm trying to pick up learning Ember at a time of particular fluctuation. The recent "Road to 2.0" blog post has helped me clarify which direction to head, but I'm struggling to validate my approach to Ember at a high level.
I want to be sensitive to people's time. My full code is here for anyone interested in providing more specific feedback (would love), but I'm mostly interested in this high level feedback on my app's structuring and my utilization of Ember's capabilities.
App Background:
I'm working on a user-to-many chat to text SMS app. Visually, each user has multiple chat windows (a Conversation) open w/ messages (Message) specific to a Profile message history. The rails backed sends messages to the target Profile. This project is very much in development.
Key Questions:
What is the best way to associate a model with a component? I'm passing each conversation model to a conversation component. As my component logic becomes so tightly integrated to the view, tt seems like a component class is taking on too much heft outside of UI. I'm starting to add of logic around how UI bubbles up to the model, but wonder if there are better approaches.
Since I'm breaking away from the proxying behavior of Array controller, I find myself referencing my model collection via this.get('content') - is there a better way to deal with the collection of conversations?
Finally, to invoke actions in a component, I've read of using Ember.Evented mixin to trigger and observe events. I.e. when a user tries to open a chat window for a profile when that chat is already open, I'd want to flash the target chat window. Is this a good way to manage these interaction in context of "Road to 2.0"?
What about passing events from the controller to the Message subcomponents? Message subcomponents would be bound to each messages' statuses (success, fail, etc). I imagine i'd just bind some message display to a record's state and status attribute. Any way I could do it better?
I'm super open to feedback. Be harsh! :)
High level code:
(full code)
ChatApp.Router.map(function () {
this.resource('conversations', { path: '/' });
});
ChatApp.ConversationsRoute = Ember.Route.extend({
model: function () { //this is a collection of active conversations
},
activate: function() { //listens to event stream
}
});
ChatApp.ConversationsController = Ember.Controller.extend({
actions: {
openChat: function(user_id, profile_id){ //open chat if one isn't open.
}
},
removeExcessChats: function(){ // removes chats that don't fit in window
},
});
ChatApp.ConversationHolderComponent = Ember.Component.extend({
actions: {
markInactive: function(){
// referencing a passed in conversation is the only way I know to reference the model.
this.get('conversation').markInactive();
},
createMessage: function(){
}
},
isFlashed: false
});
Component templates:
<script type="text/x-handlebars" data-template-name="components/conversation-holder">
<button {{action "markInactive"}}>close</button>
<h3>Profile: {{conversation.profile}} Conversation: {{conversation.id}}</h3>
<ul class="list-unstyled">
{{#each message in conversation.messages}}
<li><strong>{{message.type}}</strong> {{message.body}}</li>
{{/each}}
<li>
<form class="form-inline" {{action "createMessage" on="submit"}}>
{{input class="message_body" placeholder="Start typing a message..." value=conversation.new_message_body type="text"}}
{{input class="btn" type="submit" value="Send"}}
</form>
</li>
</ul>
</script>
<script type="text/x-handlebars" data-template-name="conversations">
<section id="todoapp">
<header id="header">
<h1>Chat Messaging</h1>
</header>
</section>
<section id="main">
<p>Open a new chat with profile id #1 <a href="#" {{action "openChat" 1 1}} >Open w/ profile 1</a> | <a href="#" {{action "openChat" 1 6}} >open profile already in convo</a></p>
<ul id="chat-app" class="list-unstyled clearfix">
{{#each conversation in model}}
<li>{{chat-holder conversation=conversation}}</li>
{{/each}}
</ul>
</section>
</script>
I didn't go through your app design, but I'm answering based on the more general Ember concepts that you mentioned.
1.
There isn't really a model object in Ember. You have a route with a model hook that returns whatever you want as your model. It can be a string, array or just a number.
When you use Ember Data, what will happen is that the model hook returns Ember Data objects.
A component can receive any object as its model/content. So, there isn't a best or worst way of associating a model and component, you just pass it what it needs.
2.
If your component is starting to get too big, probably you should split it in two or more components. Nothing wrong with having a component's template render other components.
Also, if you have logic that is shared among many components, you can refactor that into a mixin and include it in each component.
3.
Your idea for message passing between the controller and the components is *probably* right. The usual flow in Ember apps is events up & data down. Since the controller is at a higher level than a component, you can't send event in that direction, but by updating bound values you can pass new info to the components.
Ember docs say to define a store like this
MyApp.Store = DS.Store.extend();
If you are looking up records in components, this doc says you can inject the store into the component like this
// inject the store into all components
App.inject('component', 'store', 'store:main');
However, I am using the local storage adapter which I define like this
App.ApplicationAdapter = DS.LSAdapter.extend({
namespace: 'my-namespace'
});
Therefore, I don't know how to inject this into the component (where I need to look up a record) following the above instructions.
Following the instructions of this SO answer, I tried to inject the store into a component by passing it in like store=store and/or store=controller.store
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=store}} </li>
or
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=controller.store}} </li>
The goal was then to be able to do this in an action in the componeent
var todo = this.get('store');
console.log(todo, "the new store");
todo.set('notes', bufferedTitle);
console.log(todo, "todo with notes set");
todo.save();
However, todo.save(); always triggers
Uncaught TypeError: undefined is not a function
Notice that I logged the store? this is what it shows
Class {_backburner: Backburner, typeMaps: Object, recordArrayManager: Class, _pendingSave: Array[0], _pendingFetch: ember$data$lib$system$map$$Map…}
If i inspect it(by opening the tree, which isn't shown here), it does indeed show that notes were set via todo.set('notes', bufferedTitle); however, it doesn't have any of the other attributes of my model that I defined for the index route, and this object doesn't have a 'save' method. Therefore, it doesn't seem to be the actual store, but rather just some backburner object.
I got the same results trying this SO answer where it says to get the store of the targetObject
var todo = this.get('targetObject.store');
Note, I also tried this, i.e. setting the store to be the store of the item.
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=item.store}} </li>
It should be noted that if I set the store in the component, I can print the store on the page by doing {{store}} which gives me
<DS.Store:ember480>
but I can't do var todo = this.get('store'); in the action that handles the click even in the application code.
Question, using the localStorage adapter, how am I able to look up a record in a component (with the aim of then being able to alter the record and then save it again)
Note, if it's important, I define a model for the (index) route like this
App.Index = DS.Model.extend({
title: DS.attr('string'),
version (unfortunately I don't know what version of Ember data or the adapter I'm using)
Ember Inspector
1.7.0
Ember
1.9.1
Ember Data
<%= versionStamp %>
Handlebars
2.0.0
jQuery
1.10.2
Update in response to request for more info
The code that sets up the problem is very simple.
here's the router (with a bad name for the resource :)
App.Router.map(function(){
this.resource('index', { path: '/'});
}
Here's the route that gets the record to use in the Index route
App.IndexRoute = Ember.Route.extend({
model: function{
var resource = this.store.find('index');
return resource;
}
});
I have an Index Controller which does nothing in particular for the component (unless I should be defining methods on the Controller that get triggered by component events)
In the html, I do this with handlebars to pass data to the component
{{#each item in items}}
<li> {{my-component id=item.customid data=item.stats notes=item.notes store=store}}
{{/each}}
Then, in components/my-component, I have a label that when clicked is supposed to trigger an action that will let me edit one of the attributes on the model
<label> {{action "editTodo" on="doubleClick">{{notes}}</label>
that click triggers this code in App.MyComponent, which triggers the error that prompted this question
var todo = this.get('store')
todo.set('notes', bufferedTitle);
todo.save()
IMHO injecting store into components is not the best idea... By design, components should be isolated and shouldn't have any knowledge about the store.
In the doc you've given, it's written: In general, looking up models directly in a component is an anti-pattern, and you should prefer to pass in any model you need in the template that included the component.
However, if you really need it for some reason, then why not just to pass the variable store to the component?
{{my-component store=store}}
Then, you can pass the store from your controller only in the components where you really need that.
Injecting the store in all your components will most likely lead you to the bad design (although it seems tempting at first).
Here's an updated answer for Ember 2:
Ember Data's store is now a Service, and we can easily inject it into all Components via an Initializer, e.g. app/initializers/inject-store-into-components:
export function initialize(application) {
application.inject('component', 'store', 'service:store');
}
export default {
name: 'inject-store-into-components',
initialize,
}
Then, in your Components, you can access the store with this.get('store'). The obviates the need to directly pass the store as an argument to Components, which requires a lot of boilerplate in your templates.
Whilst the accepted answer is sensible for simple applications it is perfectly acceptable to inject a store into a component if that component doesn't have a relationship with the url, like side bar content or a configurable widget on a dashboard.
In this situation you can use an initializer to inject the store into your component.
However, initializers can be a pain to mimic in testing. I have high hopes that the excellent Ember.inject API that is testing friendly will extend beyond services and accommodate stores. (Or that stores will simply become services).
According to this docThe preferred way to inject a store into a component is by setting a store variable to the record, for example
{{#each item in arrangedContent}}
<li> {{my-component store=item}} </li>
{{/each}}
Then in application code, you can do
var store = this.get('store');
store.set('todo', bufferedTitle);
i try to create my first ember.js app. A calendar-
my day model
App.Day = Ember.Object.extend({
today : null,
dayNumber : null,
addEvent : function() {
console.log(this);
$("#myModal").modal('show');
}
});
the html view
<div class="cal">
{{#each App.DayList}}
{{#if this.today}}
<div class="day today" {{action "addEvent" target="model" }}>
{{#with this as model}}
<span class="text">{{this.dayNumber}}</span>
{{/with}}
</div>
{{else}}
<div class="day" {{action "addEvent" target="model" }}>
{{#with this as model}}
<span class="text">{{this.dayNumber}}</span>
{{/with}}
</div>
{{/if}}
{{/each}}
</div>
so on click on day i show the bootstrap dialog and I wont to load extern data, but I need a information about clicked day.
My understanding is I create a view
App.DayDetails = Ember.View.extend({
});
and inside this view I send an ajax request, but how to get information about clicked day inside this view?
You should almost never be doing any AJAX in a view.
Views do two things:
(1) draw themselves
(2) respond to UI events (clicks, typing, etc)
Your view should get its contents from a controller, in this case I suppose App.DayController or DayDetailsController. (that's another thing, it's best practice to end your subclasses with View or Controller, etc, so its obvious at a glance what they do).
Where the controller gets that data from is where things might get complicated. Ideally, in a mature app, you'd have a data store (a combination—in concept—of your server-side database and ActiveRecord, if you use rails) that would be queried. Simplistically, however, you could have the controller be responsible for using jQuery to manually handle an ajax request. So long as we're taking short-cuts, you could put such a call in a number of place, (a singleton controller, a day-specific item controller, the day model itself), just NOT the view. And it's important when taking these sorts of short-cuts to limit the contagion... all you should be doing with the manual ajax is fetching the JSON and then immediately and expeditiously getting it back into the ember ecosystem by setting it as the content of an array controller. I.e., no going one or two steps further by trying to insert the data into a view manually or whatnot. Don't fight Ember, if you can avoid it.
A few things:
(1) Your use of this is superfluous, as are the {{with}} statements. Inside an {{each}} block the context will be the current object (or its wrapping controller, if you're using itemController) in the iteration. (UNLESS you use "x in y" syntax, in which case the context remains the controller)
(2) The model should NOT be attempting to modify the DOM. Instead, rely on bindings and your controllers to coordinate UI changes. What you might want to do is have a App.DayController that you can put addEvent on, and then in your {{each}} use itemController="App.DayController".
App.DayController = Ember.ObjectController.extend({
addEvent: function () {
// ...
}
});
Then, the context for each loop in your {{each}} template will be each individual day controller. The controller will automatically be the target and context for the views so your template would look like this:
{{#each App.DayList itemController="App.DayController"}}
<div {{bindAttr class=":day today"}} {{action addEvent}}>{{dayNumber}}</div>
{{/each}}
(the : in :day means that day will always be a class, but today will only be a class if the today property on the context is truthy)
Because each day sends addEvent to its own controller, there's no need for figuring out what day to load.
I'm having trouble doing things on Ember, and I'm sure it's because I still haven't fully grasped "the Ember way" of doing things, and I'm a trying to do a couple of things that get out of the scope of standard tutorials out there.
I'm developing some sort of textfield-with-suggestions component that will appear in every single page of my web app. I won't ask here all the details on how to do it, but just a couple specific things I am having trouble accomplishing from the start. The following are the relevant code fragments of what I have so far.
// handlebars template: searchbar.hbs
{{view App.SearchField viewName="searchField"}}
<div class="results" {{bindAttr class="searchField.hasFocus"}}>
This is where the results for whatever the user has typed are shown.
</div>
// coffeescript source of the views: searchbar.coffee
App.SearchBar: Ember.View.extend
templateName: 'searchbar'
App.SearchField: Ember.TextField.extend
placeholder: 'Search'
hasFocus: false
eventManager: Ember.Object.create
focusIn: (event, view) ->
#set('hasFocus', true)
focusOut: (event, view) ->
#set('hasFocus', false)
// somewhere in the layout of the pages in my app
<div id="header">
{{App.SearchBar}}
</div>
This will probably also need a controller, but I haven't developed it yet, because I don't know where it fits within this setup yet.
First, I want the suggestions popup panel to appear as soon as the search field obtains focus. That's the reason of my attempt above to implement a hasFocus property on the searchfield. But how do I achieve making my div.results panel react to the focus state of the input field?
In general, and this is the core of my question here, how do I connect all things to develop this component? If the answer is by attaching it to a controller, then how do I setup a controller for this component, and how do I specify that it is the controller for it, so that it acts as a context for everything?
I think you have to clearly separate concerns. Stuff related to the view (ie manipulating the DOM with jquery) should stay in the view. Stuff related to the application state should be in the controller. Though,in your case, I think you can simply bind an observer on the hasFocus property, and show the suggestions. Something like:
App.SearchField: Ember.TextField.extend
placeholder: 'Search'
hasFocus: false
eventManager: Ember.Object.create
focusIn: (event, view) ->
#set('hasFocus', true)
focusOut: (event, view) ->
#set('hasFocus', false)
focusDidChange: (->
if #hasFocus
$('.results')... // here I let you do the suggestion stuff
// based on data retrieved from the controller
else
// probably hide the results div.
).observes('hasFocus')