Meteor: ¿Hay un alcance reactivo por plantilla?
handlebars.js (4)
Estoy renderizando la misma plantilla de Manillares en múltiples ubicaciones (arbitrariamente muchas) en la misma página. Dentro de cada plantilla, quiero un botón para alternar la visibilidad de un div. Cuando guardo este estado con Session.set
, al hacer clic en un botón, obviamente se alternan todos los divs en todas las instancias de la plantilla que no se desean.
Podría guardar el estado en el contexto de datos de la instancia de plantilla (que está vinculada a this.data
en las devoluciones de llamada Template.myTemplate.rendered
y Template.myTemplate.created
), pero hay dos problemas con eso.
-
this.data
no es una fuente de datos reactiva, por lo que no se propagará a la división - No tengo acceso a la instancia de plantilla en
Template.myTemplate.events
(como se describe en meteor-core )
Finalmente, de alguna manera podría guardarlo en una colección. Pero, ¿cómo identificaría el estado de cada plantilla representada? También podría haber una forma de pirateo con jQuery, pero esa no es la forma en que quiero desarrollar aplicaciones Meteor que se supone que funcionan con plantillas reactivas. Especialmente, cuando esa plantilla se vuelve más compleja.
¿Me estoy perdiendo algo o realmente no hay equivalente a los controladores de AngularJS que pasan un $scope
para cada instancia de plantilla?
Actualización : estaba pensando en algo como esto.
template.html:
<template name="jsonObject">
<input type="button" />
<div class="{{hidden}}">content</div>
</template>
cliente.js:
Template.jsonObject.hidden = function(){
var ret = "";
if (this._hidden) {
ret = "hidden";
}
return ret;
};
Template.jsonObject.events({
''click input'' : function(event, template){
template.data._hidden = true;
}
});
Así es como conseguí una fuente reactiva por plantilla.
Solo quiero preguntarle al usuario el motivo de la extinción de un dinosaurio cuando su estado de extinción está configurado para extinguirse.
<template name="dinosaur">
<label for="extinction-status">Extinction status</label>
<select name="extinction-status">
<option value="not-extinct">Not extinct</option>
<option value="extinct">Extinct</option>
</select>
{{#if isExtinct extinctStatus newExtinctStatus extinctStatusDep}}
<label for="extinction-reason">Reason for extinction</label>
<input name="extinction-reason" type="text" placeholder="Why is it extinct?"/>
{{/if}}
</template>
Para mostrar de forma reactiva el campo de razón, agregamos Deps.Dependency
a this.data
en la función de plantilla created
Template.dinosaur.created = function() {
this.data.newExtinctStatus = null;
this.data.extinctStatusDep = new Deps.Dependency;
};
Escuchamos cuando el usuario cambia la selección del estado de la extinción, y actualizamos el nuevo Estado de newExtinctStatus
y el llamado en nuestro Estado extinctStatusDep
.
Template.dinosaur.events({
''change [name="extinction-status"]'': function(event, template) {
var extinctStatus = $(event.target).val();
template.data.newExtinctStatus = extinctStatus;
template.data.extinctStatusDep.changed();
}
});
En el ayudante decimos que dependemos de los Deps.Dependency
. Deps.Dependency
que se nos pasa
Template.dinosaur.helpers({
isExtinct: function(status, newStatus, statusDep) {
if (statusDep) {
statusDep.depend();
}
if (newStatus) {
if (newStatus == ''extinct'') {
return true;
}
else if (status == ''extinct'') {
// No new status is set, so use the original.
return true;
}
}
});
Es un poco de un truco que implica agregar a this.data
, pero permite una reactividad por plantilla, útil para cuando usted confía en datos que aún no se han guardado en algo en una Collection
.
Creo que la discusión sobre meteor-core está desactualizada (aunque es reciente). Según los documentos (citados a continuación), esto debería ser posible:
Template.myTemplate.events({
foo: function(event, template) {
var _div = template.find(''...'');
}
});
La función del controlador recibe dos argumentos: evento, un objeto con información sobre el evento y plantilla, una instancia de plantilla para la plantilla donde se define el controlador. El controlador también recibe algunos datos de contexto adicionales en esto, dependiendo del contexto del elemento actual que maneja el evento. En una plantilla de Handlebars, el contexto de un elemento es el contexto de datos de Handlebars donde se produce ese elemento, que se establece mediante ayudantes de bloque como #with y #each.
Actualizar
Suponiendo que cada div ocultable tuviera una identificación única, ¿qué pasaría si intentara algo como esto? No lo he probado.
Template.jsonObject.hidden = function(){
var _id = $(".classOfPossibleHiddenDiv").prop("id");
return Session.get(_id) || false;
};
Template.jsonObject.events({
''click input'' : function(event, template){
Session.set($(template.find(".classOfPossibleHiddenDiv")).prop("id"), true);
}
});
Todas las demás respuestas son demasiado complicadas y / o están desactualizadas. A partir de Meteor 1.0, la solución recomendada para mantener el estado por plantilla es usar las variables reactivas almacenadas en la instancia de la plantilla :
Un objeto de instancia de plantilla representa una aparición de una plantilla en el documento. Se puede usar para acceder al DOM y se le pueden asignar propiedades que persisten a medida que la plantilla se actualiza reactivamente. [...] puede asignar propiedades adicionales de su elección al objeto.
Las variables reactivas son proporcionadas por el paquete de núcleo reactive-var
:
Un ReactiveVar tiene un solo valor que se puede obtener y establecer, de modo que el
set
llamadas invalidará cualquier cálculo que se llame, según el contrato habitual para las fuentes de datos reactivos.
Su código se convierte en:
Template.jsonObject.onCreated = function () {
this.hidden = new ReactiveVar(false);
};
Template.jsonObject.helpers({
hidden: function () {
return Template.instance().hidden.get() ? ''hidden'' : '''';
}
});
Template.jsonObject.events({
''click input'': function (event, template) {
template.hidden.set(true);
}
});
El HTML es el mismo que cabría esperar:
<template name="jsonObject">
<button>Click Me</button>
<p>Hidden is {{hidden}}.</p>
</template>
RESPUESTA ACTUALIZADA - METEOR 0.8.3
Meteor ahora proporciona la función UI._templateInstance()
, que da acceso a la instancia de plantilla en los ayudantes de plantilla. Esto permite una implementación mucho más limpia:
1. En el siguiente código HTML, elimine {{#with templateInstanceId=getTemplateInstanceId}}
2. Reemplace el código JS por este:
var templateInstanceId = 0;
Template.divWithToggleButton.created= function()
{
this.templateInstanceId = ++templateInstanceId;
};
Template.divWithToggleButton.events(
{
''click button'': function(event, template)
{
Session.set("visible", template.templateInstanceId);
},
});
Template.divWithToggleButton.visible= function()
{
return Session.equals("visible", UI._templateInstance().templateInstanceId);
};
RESPUESTA ANTERIOR Tenía el mismo requisito en mi proyecto: identificar de forma única una instancia de plantilla para usar con Session.set
.
Aquí está mi truco simple, usando reemplazo de contexto y una identificación de instancia única ''deslizante'' (más sobre esto más adelante):
En cualquier lugar del código del cliente, coloque:
var templateInstanceId = 0;
UI.registerHelper(''getTemplateInstanceId'', function()
{
return templateInstanceId++;
});
Luego use {{#with templateInstanceId=getTemplateInstanceId }}
en las plantillas que desee para identificar las instancias de forma única. Con tu ejemplo, podría ser algo como esto (no probado):
HTML:
<!-- Div with a toggle visibility button. Use as a Block Helpers (with # instead of >) -->
<template name="divWithToggleButton">
{{#with templateInstanceId=getTemplateInstanceId }}
<div>
<!-- The ''toggle visibility'' button -->
<button type="button">Toggle Visibility</button>
<!-- The div content -->
{{#if visible}}
{{> UI.contentBlock}}
{{/if}}
</div>
{{/with}}
</template>
JS (código de cliente):
Template.divWithToggleButton.events(
{
''click button'': function(event, template)
{
Session.set("visible", this.templateInstanceId);
},
});
Template.divWithToggleButton.visible= function()
{
return Session.equals("visible", this.templateInstanceId);
};
Ahora sobre este extraño ''deslizante'' id de instancia única:
Esta identificación se actualiza en cada representación de la plantilla, no encontré otra manera. Esto significa que cualquier nueva representación invalidará la identificación única almacenada en la sesión . La nueva representación ocurre si su div contiene fuentes de datos reactivas. Dependiendo de su caso, esto puede ser un problema o no.