I have a template where recipes are rendering through {{#each recipes}} and I'm using ReactiveVar to toggle the edit form of each recipe from hide to show. Everything works fine, but I want that when I press edit button in one recipe then all other recipe forms, which were opened before, will set to hide
Template.Recipe.onCreated(function(){
this.editMode = new ReactiveVar(false);
});
Template.Recipe.helpers({
editMode: function() {
return Template.instance().editMode.get();
}
});
Template.Recipe.events({
'click .fa-pencil': function(event, template) {
//Right here I guess should be something that switches all "editMode" to false
template.editMode.set(!template.editMode.get());
},
});
I think you need a different approach
a Session variable which has the Id of the current editing recipe and then helper on the template to see if the current recipe is the one being edited
add a property to the recipe whether its being edited or not, then update the collection when you change which one is edited
A common object with a reactive var, and much the same technique as the Session variable. If you have a parent template, then it could live there (this is probably the nicest option)
In order to achieve this functionality, you should think about how your templates communicate. One template shouldn't be able to set the state for it's siblings, which means you are thinking it wrong.
You should store that state somewhere global so all templates can see and react to it. That option would mean using Session (as Keith mentioned) or a route parameter (better option, in my opinion).
Another option is to use the parent to store that data, using events to communicate between parent and children. A child template can send and event to the parent who direct another event to each other child.
Related
Context
Let's say I have page that displays a list of employees, along with many other buttons and components. and whenever I click on an employee, a side panel appears with a bunch of information about that employee. If I click on any other employee while an employee is selected, the side panel remains intact with only the information inside it being re-fetched and updated.
I can also, change the employee's information from that side panel by editing the different fields and clicking Update which bulk updates every updated fields.
Problem Statement
Whenever an employee's information is being edited, I can also click on another employee, which, of course, means that the edited data for the previous employee is lost.
Objective
What I need to do in this case is that, whenever an employee's information is being edited from the side panel (a flag is present to denote that), and if any other component/buttons apart from Update is clicked, then I need to show a modal which states that everything that is unsaved would be lost. If the user selects Ok, the component that was clicked should mount/rendered/perform whatever it does. Otherwise, the user should go back to the previous state.
I initially tried to achieve that by using componentWillUnmount of the sidepanel, but it didn't really work because that component will unmount anyway. What I am thinking is that, I need to put a onClick handler in every button/component now apart from that 'Update' which checks if any information is being edited, if yes it will render that component, if not it will render that modal, and the user decides what to do in that modal. I am very skeptic about this as well by placing checks everywhere for a minor thing.
Anyone with experience about related to this use case ? Any insights would be highly appreciated. :)
First Put isEdited state isEdited: false Then when you edit the post then set isEdited to true. When user clicks update then set isEdited back to false. When user clicks button other than update then check isEdited if isEdited is true then dispay modal with message and button when button of model is click set isEdited to false.
Hopefully this will help
There are a few ways I can think to do this.
Add a check at the start of the function for each buttons onClick. As you've mentioned already, this is tedious, especially if there are lots of buttons that should affect the state.
Create a function that wraps the onClick functions, to perform this check. Similar to 1, however a bit easier to repeat:
const myOnClick = newEmployeeNumber => {
myState.employeeToView = newEmployeeNumber;
}
const checkUserWantsToContinue = (onClickToRunWhenContinued) => {
if (myAppState.isEdited) {
promptUserToSaveFirst();
} else {
onClickToRunWhenContinued();
}
}
return (
<div onClick={checkUserWantsToContinue(myOnClick)} />
);
You mentioned that you are trying to put this into the componentWillUnmount function. That implies that something is making the component unmount. Can you add the check in there? So rather than checking with the user before the component unmounts, check with them before the action that makes the component unmount.
Consider a VueJS app that allows for showing / editing blogposts.
On clicking "edit blogpost" a modal (Vue component) is shown to edit the current blogpost (just an ordinary js-object).
To this end I pass the current blogpost as a prop to the modal-component.
The Modal component has a couple of form fields that should be populated with the blogpost-attributes.
However, the Vue 'way' is to have a data object on the Modal-component that acts as the model to populate the form fields, and vice-versa, be updated when the user changes the form fields.
The problem: How do I connect the blogpost which is passed as a prop to the data field of the modal-component so that:
the blogpost populates the form fields
the blogpost gets updated when updating the form fields
What's the correct Vue way to accomplish this?
You have three (at least) ways of doing it:
1) Connect the data via the prop attribute, just as you are doing, but add the .sync attribute to it. This way, when the data is modified on the form, it automatically gets modified too on the component. The problem with this solution is that the update is automatic, so if a validation fails, or the user closes the modal without saving the changes, these are saved anyway. An example: https://jsfiddle.net/Lz3aq64f/
2) The other way of doing it is getting the modal to $dispatch an event with the saved information when it's saved. The blogpost element should listen for this event and act accordingly.
On the modal:
this.$dispatch('update-post', this.title, this.author);
On the blogpost:
this.$on('update-post', function(title, author) {
this.title = title;
this.author = author
});
If the blogpost component and the modal component are not in the same hierarchy, things get a little bit more complicated, and probably you need the common parent element to act as a proxy. Think of the modal element dispatching the information up, the #app element catching it via $on, and then doing $broadcast to the blogpost element.
3) Use something like vuex to act as a central repository of state. I don't know how big is your application, but this would be the cleanest way to go: http://vuex.vuejs.org/en/index.html
Good luck!
In my Ember app, I initially start at the root level (/) and I have multiple links (say Link1, Link2, Link3)
Now each of these links displays a common grid i.e. I use same route/controller/template JS, but re-render the grid, by setting some attributes dynamically on the controller
Thus in my application.js, I do
this.controllerFor('my-grid').set('attr1', params[0].value);
this.controllerFor('my-grid').set('attr2', params[1].value);
this.transitionTo('my-grid');
Is this the correct way to transition ?
I mean, specifically I need
this.transitionTo('my-grid');
to be called only once (say on click of link1 from root), since after then, clicks on link2, link3 would just need a change of attribute values on my same controller and should just re-render the grid (after server api call)
Please suggest if there is some condition that I can check for calling
this.transitionTo('my-grid');
In your application you can check ApplicationController.currentPath:
this.controllerFor('my-grid').setProperties({
attr1: params[0].value,
attr2: params[1].value
});
if (this.controllerFor('application').get('currentPath') !== 'my-grid') {
this.transitionTo('my-grid');
}
I have a component say {{component-1}} which gets called many times and creates a custom-texbox container and label as many times as it gets called.
Now whenever a user writes something in it a suggestion box should appear below it. Since the suggestion box can be reused everywhere i dont want to have a separate suggestion box for each {{component-1}}, rather i want to have another component called {{suggestion-box}} that gets inserted inside component-1 i.e. the textbox container.
I dont want {{suggestion-box}} to be inside dom at all since it is needed only when somebody types in it. I want to add/insert it into {{component-1}} when someone types. Instead of a component i even tried to use a view
Here are the different things i have tried and failed
Note:
suggestionBox is the component
textbox-container is an element inside {{component-1}}
Inside {{component-1}}this.$().find(".textbox-container").append(this.suggestionBox );where this.suggestionBox = suggestionBoxComponent.create(); I have event tried suggestionBoxView.create();It gives me the error that i one view cant be inserted into another and i need to use containerView
var tmp = Ember.Handlebars.compile('<div class=".suggestionBox"></div>');this.$().find('.textbox-container').append(tmp());I get the error called
Uncaught TypeError: Cannot read property 'push' of undefined
I even tried to use view instead of component i.e. make suggestionBox a view but then again i cannot insert one view inside another
I have tried a lot more things.
Few points:
I dont want alternate solutions of how textbox and suggestion box could be created
How to pass information from a component or a a template to a view? Say i do {{view "suggestion-box"}} inside component-1 template, how do i pass values to it? Say for components we pass in the context like this {{component1 sampleVar1=val1 sampleVar2=val2}}
i Want to know how to programmatically add a component or a view and if it is a view how to pass the data to it?
I dont want to use container-view since it will cause more complexities, however if your solution allows me to pass value from {{component-1}} to container-view and inturn pass it to corresponding childView1 and childView2 then that solution is acceptable
Just an update:
I even tried to use a view container inside the {{component-1}}
I also tried to use view block inside {{component-1}} i.e.
{{#view "view-name"}}
----earlier component elements here-----
{{/view}}
In both the above points "view-name" is a ContainerView which is getting inserted properly but the component element are not getting inserted
This can be achieved with a ContainerView and the viewName attribute. The component or view can access the ContainerView or any other view that is part of a template through its assigned viewName.
Example,
hbs
{{view Ember.ContainerView viewName="my-menu-container"}}
js - then from the component or view
this.get("my-menu-container").pushObject(suggestionBoxViewOrComponent);
http://emberjs.jsbin.com/yoyujeqi/1/edit
p.s. the context of the example is not relevant to the suggestion box, sorry about that, as it has been extracted from another answer related to adding a menu view dynamically.
i have my main page and also two partial views. One partial view is a menu and the other is a telerik grid.
What i want to achieve is selecting a row in the grid and when i click a button in the menu i want the page to navigate to that action passing the selected row (id).
i want to refresh the entire page and not only the div with the grid.
I tried using document.location = "/Pedido/DetalhePedido/" + id; but i don't receive the id n the controller.
I also tried using $.get('#Url.Action("detalhePedido", "Pedido")', data, function (result) { }); usually i use this to refresh a div and i can't seem to make this work with the entire page (and it probably shouldn't ).
Wich methods do you usually use in your web apps to reproduce this sort of behaviour?
Because clicking on the row happens on the browser, you can't depend on anything from the controller or model unless it's already somewhere in your original model. I implement this by adding a hidden Id column to the grid and model it uses for rendering, then add client events and handlers to the grid and view. Check out the samples provided for the Grid under Client-Side Events for some of the different client events you can take advantage of.
On your grid, first add the Id column (hidden if you like):
.Columns(columns =>
{
columns.Bound(o => o.Id).Hidden(true);
}
Then add ClientEvents and wire up onRowSelect like so:
.ClientEvents(events =>
{
events.OnRowSelect("onRowSelected");
}
Then add a function to handle this event like so:
function onRowSelected(e) {
var id = e.row.cells[0].innerHTML;
window.location = "Something/Details/" + id;
}
UPDATE
Sounds like you are doing everything right on the client. The problem is likely elsewhere, in your Action or Bindings. But to be certain, take a look at what /Pedido/DetalhePedido/" + id actually is with an alert before venturing down that path. You should be able to take that and enter it directly into the url of your browser and hit your action as long as your action is correctly defined, accepting an int called id.
If its still a problem, you need to look at you action. Is it marked for Post? If it is Window.Location wont work because it's not a post. Is the argument it accepts named Id and of type int? If not, should it be? Have you changed your Routes, and if so does your Url match any routes defined?