Migrating to FlowRouter, need something similar to a template data context - node.js

So I've read a lot on the discussion of Iron Router vs FlowRouter.
I started my project using Iron Router, but since changed my mind and I'm currently migrating to FlowRouter.
Everything was going smoothly until I started migrating the comments section of my app. You see, this section is reused several times on the app, it serves as a comment section for news, posts, photos, videos, etc.
Example using IR's data context:
Router.route('/news/:slug', {
name: 'newsItem',
waitOn: function() { Meteor.subscribe('news.single', this.params.slug) },
data: function() {
return News.findOne({slug: this.params.slug});
}
});
<template name="newsItem">
<p>{{title}}</p>
<p>{{body}}</p>
{{> commentSection}}
</template>
The Comment collection schema has a "type" (to know to what type of "thing" this comment belongs to, news, photos, etc). That type was set on the "form .submit" event of commentSection template. Example:
'submit form': function(e, template) {
e.preventDefault();
var $body = $(e.target).find('[name=body]');
console.log(template.data.type);
var comment = {
type: template.data.type,
parentId: template.data._id,
parentSlug: template.data.slug,
body: $body.val()
};
Meteor.call('insertComment', comment, function(error, commentId) {
if (error){
alert(error.reason);
} else {
$body.val('');
}
});
}
This worked because the template data context contained the News item which in turn has a a type property as well.
How could I achieve something similar to this only using Flow Router without setting data on the template as it is recommended by the official guide?

You'll want to use a template subscription and a {{#with}} helper probably.
Template.newsItem.onCreated( function() {
Template.instance().subscribe('news.single', FlowRouter.current().params.slug);
});
Template.newsItem.helpers({
item() {
let item = News.findOne();
if( item ) {
return item;
}
}
});
<template name="newsItem">
{{#with item}}
<!-- Your existing stuff -->
{{/with}}
</template>

Related

Trying to get id parameter with nodejs and acces it in angular

I am trying to access a detail page that i have for a list of items in my html(ionic). So when I click on 1 item I need to go on a detail page with information about that specific item. I am using mongo database for the items , the model of the items are : name and ingredients.
I am stuck here and don't know what to do , please help if you have a bit of time , thank you.
My code :
<ion-list *ngFor="let recipe of recipes ; let i = index">
<ion-item>
<ion-label>{{recipe.name}}</ion-label>
</ion-item>
</ion-list>
Request for the database trying to get the id to access the item (I get the
public findByName(id: string) {
return this.http.get<any>(
"http://localhost:9020/:id" + id
);
}
This is what I tried in typescript
this.route.paramMap.subscribe((paramMap) => {
if (!paramMap.has("id")) {
this.navCtrl.navigateBack("/home");
return;
}
this._recipeDetail = this.nutService
.findByName(paramMap.get("id"))
.subscribe((id) => {
this.id = id;
});
});
Also in route module :
{
path: "name/:id",
loadChildren: () =>
import("./r-d/r-d.module").then(
(m) => m.RDPageModule
),
},
But its not working , maybe I don't have the right approach? Can someone please guide me a bit ?
Thank you for your time
this._recipeDetail = this.nutService
.findByName(paramMap.get("id"))
.subscribe((id) => {
this.id = id;
});
You should be expecting your api to return you the recipeDetail.. you are not storing it properly.
this.nutService.findByName(paramMap.get("id"))
.toPromise().then((data) => {
this.recipeDetail = data;
});
And then, you can display your recipeDetail inside your template. I have also changed .subscribe() to .toPromise().then(). This is a better approach to avoid memory leaks in your app.

Why is the vuex component isn't updated, despite the state updating?

When I load my page, I fetch a list of optboxes items.
Sources
Project's sources are online:
optboxes page ;
store (store, actions, mutations, getters).
optboxes pages
The HTTP request is well send and return adequate data:
created(){
this.getOptboxes();
},
components: {
'optbox': OptboxComponent,
},
methods: {
getOptboxes() {
optboxes.all().then((response) => {
this.setOptboxes(response.data.output);
}).catch(() = > {
this.no_optbox_message = 'there is no optbox';
logging.error(this.$t('optboxes.get.failed'))
});
}
},
vuex: {
actions: { setOptboxes: actions.setOptboxes},
getters: { optboxesList: getters.retrieveOptboxes}
}
I'm iterating over the results as follow:
<div v-for="optbox in optboxesList" class="panel panel-default">
<optbox :optbox="optbox"></optbox>
</div>
Store
const state = {
optboxes: {
/*
'akema': {
hostname: "192.168.2.23",
id: "akema",
printers: [
{
description: "bureau",
destination_port: 9100,
forward: "normal",
hostname: "1.2.3.4",
id: 0,
listening_port: 9102
}
]
}
*/
}
};
Question
If I switch to another pages and come back then the list appear. I also notice that with the Vuex extension I can commit the state and see the changes.
Why are my changes not applied automatically?
I had to change my data structure due to Change Detection Caveats.
Due to limitations of JavaScript, Vue.js cannot detect the following
changes to an Array:
When you directly set an item with the index, e.g. vm.items[0] = {};
When you modify the length of the Array, e.g. vm.items.length = 0.
Store
optboxes is now an array.
const state = {
optboxes:[]
}
Then update my mutations accordingly to edit the array.
Maybe it's an issue of reactivity?! I assume your setOptboxes mutation is not being picked up by vue:
setOptboxes(state, optboxes) {
for (var optbox of optboxes) {
state.optboxes[optbox.id] = optbox;
}
}
You can read about it here:
https://vuejs.org/guide/list.html#Caveats
https://vuejs.org/guide/reactivity.html
The docs solution is to use:
state.optboxes.$set(optbox.id, optbox);
which will trigger a view update.
Using Vue.set dit it for me. Take a look here

Search information by user on firebase

This is probably very simple ,so sorry...
I'm trying to create an application where i can create users and store informations by Firebase. There is an example here :
http://image.noelshack.com/fichiers/2015/11/1426182307-log.png
But now I want to check if the name already exists if someone wants to create a new user.
How would I go about grabbing individual user information without knowing their id , like simplelogin:54?
I've found this topic Get users by name property using Firebase but it is not the same thing because in my case I don't know the children after "Users"
Cheers,
Like Frank said, you must know something about the user to be able to look her/him up.
However, here is a general answer. I am assuming that by "name" you mean the property "identifiant" that you've created.
Suggestions
Start by looking over the Firebase Query documentation.
Short Answer
To check if a user exists by the identifiant property, you'd orderByChild("identifiant") and query for a specific user with the .equalTo("<identifient_here>").
For example, to check if a user with identifient="aaa",
var usersRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/Users");
var identifient = "aaa";
usersRef.orderByChild("identifiant").equalTo(identifient).once("value", function(snapshot) {
console.log("Loaded user by identifient:",identifient,snapshot.val());
});
If instead you want to query by the key (such as simplelogin:53), you could query by using orderByKey() instead of orderByChild()... or just simply setting the ref to the user's key like so:
var userKey = 'simplelogin:53';
var userRef = new Firebase("https://<YOUR-FIREBASE-APP>.firebaseio.com/Users" + userKey);
userRef.once("value", function(snapshot) {
console.log("Loaded user",snapshot.val());
});
Long(er) Answer
You can handle this with a user factory (see Angular Providers documentation).
You return a promise in the factory using the $q service.
Here is the Angular API documentation for $q.
Example with UserFactory
Check out this working PLNKR example.
It's tied to one of my public Firebase instances.
I created the same simplelogin:53 user in /Users like you have.
If you search for the identifient = aaa, you'll get the right user.
The controller implementation here is for example purposes, and doesn't really do anything worth while. It's just for reference.
The Data
{
"Users" : {
"simplelogin:53" : {
"identifiant" : "aaa"
}
}
}
UserFactory
.factory('UserFactory', function($q, $firebaseObject, fbUrl){
return function(userId){
var deferred = $q.defer();
if (userId.isNotEmpty()) {
var userRef = new Firebase(fbUrl + '/Users/').orderByChild("identifiant").equalTo(userId);
userRef.once("value",
function(dataSnapshot){
if (dataSnapshot.val()) {
console.log("Loaded user",dataSnapshot.val());
deferred.resolve(dataSnapshot.val());
} else {
console.info("Couldn't find user by id",userId);
deferred.reject("No user found by identifient = '"+userId+"'");
}
},
function(error){
console.error("Error loading user",error);
deferred.reject(error);
}
);
} else {
deferred.reject("No id entered!");
}
return deferred.promise;
}
})
Controller
.controller('HomeController',function($scope, UserFactory) {
$scope.identifient = '';
var showError = function(errorMessage) {
if (errorMessage) {
showUser(false);
$scope.error = errorMessage;
} else {
delete $scope.error;
}
}
var showUser = function (userObject) {
if (userObject) {
showError(false);
$scope.user = userObject;
} else {
delete $scope.user;
}
}
$scope.loadUser = function() {
var userPromise = new UserFactory($scope.identifient);
userPromise.then(function(data){
showUser(data);
}).catch(function(error){
showError(error);
});
}
})
Template
<div ng-controller="HomeController">
<h2>Home Template</h2>
<input ng-model="identifient" placeholder="identifient"/>
<button ng-click="loadUser()">Find User</button>
<hr/>
<div ng-if="user">User: {{user}}</div>
<div ng-if="error">Error: {{error}}</div>
</div>
Hope that helps.

Is there a way to create a dynamic partial resulting from server treatment?

I've started to use NodeJS for a couple of months now and I came across a little problem with partials rendering.
I'd like to include a partial view in some templates but I want this partial to be dynamically generated from the server (because it depends on data retrieved from DB and other stuff).
I tried to create a template helper to do that but as the processing needs to be done asynchronously I can't get an html return to write within my template.
Basically what would be the best for me would be something similar to (this code does not work obviously):
template_file.js
...
<div>
<%- generatePartial(data) %>
</div>
...
helper_middleware.js
module.exports = function registerAppHelpers(request, response, next)
{
var appHelpers = {};
appHelpers.generatePartial = function generatePartial(data)
{
if (request.isAuthenticated())
{
DB.findOne({ id: request.user.id }, function found(error, obj)
{
if (error)
...
if (obj)
{
return generatePartial1(data);
}
else
{
return generatePartial2(data);
}
});
}
else
{
return generatePartial3(data);
}
};
// Register the helpers as local variables to be accessed within a template.
for (var helper in appHelpers) {
response.locals[helper] = appHelpers[helper];
}
next();
};
Now I may be completely wrong about the way I want to deal with this problem, so if you have any solution/other suggestions about that do not hesitate.
PS : I use ExpressJS and EJS.
I think you are going completely in a wrong direction..
What ejs is for?
ejs is javascript embedded in html so you can create dynamic html.
so whatever logic you have just write it inside the ejs template and let it handle everything. you just need to pass the information to ejs engine.
So instead of
if (obj)
{
return generatePartial1(data);
}
else
{
return generatePartial2(data);
}
I would suggest to capture the whole data
if (obj)
{
array1.push(data);
}
else
{
array2.push(data);
}
and then pass this whole bunch of data to ejs, write the conditions and all logic in ejs file, and let it handle the html logic.
for ex.
res.render('template_file.js', {
array1: array1,
array2: array2
});

Orchard CMS Contrib.Review module

I am beginner in Orchard CMS and i need add voting functionality to content. I have installed Contib.Vote and Contrib.Review modules. After that i have added Review part to page content type. Also, i have executed recipe. At the first look everything is fine, but link for review refer to the same page with # symbol and nothing is happenning by clicking on it. It seems like module does not work or work incorrectly. Please help with my problem.
UPD.
Hi devqon and thanx for your help. Your answer was really useful for me. According to your advice i was looking around javascript inside Review Part view file (Parts_Reviews.cshtml). Just for a test i changed its source code a little bit.
#using (Script.Foot())
{
<script type="text/javascript">
//<![CDATA[
(function () {
var numberOfReviewsToShowByDefault = 5;
var $showAllReviewsLink = $('#showAllReviewsLink');
var $deleteReviewConfirmationDialogDiv = $('#deleteReviewConfirmationDialogDiv');
$deleteReviewConfirmationDialogDiv.dialog({ autoOpen: false, modal: true, resizable: false });
$('#deleteReviewLink').click(function () {
$('#reviewId').val($(this).attr("data-review-id"));
ShowDeleteReviewDialog();
return false;
});
$('#showReviewFormLink').click(function () {
$('#createReviewLinkDiv').slideToggle('fast', function () { $('#reviewFormDiv').slideToggle('fast'); });
return false;
});
$('#cancelCreateReviewLink').click(function () {
$('#reviewFormDiv').slideToggle('fast', function() { $('#createReviewLinkDiv').slideToggle('fast'); });
return false;
});
$('#deleteReviewForm').submit(function () {
$('input[type=submit]', this).attr('disabled', 'disabled');
});
$('#cancelDeleteReviewButton').click(function () {
CloseConfirmationDialogDiv();
return false;
});
var rowCount = $('#reviewsList li').length;
if (rowCount > numberOfReviewsToShowByDefault) {
SetupToggle();
}
if (document.location.hash === '#Reviews') {
var topPx = $('#reviews-heading').position().top;
$('body,html').animate({ scrollTop: topPx }, 'slow');
}
if ($("#comment").length) {
var characterCountUpdater = new CharacterCountUpdater($("#comment"), $("#commentCharactersLeft"));
setInterval(function() { characterCountUpdater.UpdateCharacterCount(); }, 100);
$("#comment").keypress(function() { characterCountUpdater.UpdateCharacterCount(); });
if ($("#comment").val().length) {
$("#showReviewFormLink").trigger("click");
}
}
function CharacterCountUpdater(commentBox, charactersLeftBox)
{
this.commentBox = commentBox;
this.charactersLeftBox = charactersLeftBox;
this.maxLength = commentBox.attr("maxlength");
commentBox.removeAttr("maxlength");
return this;
}
Now form for review is displayed. The form looks good, submit button works, character counter works too. But i still can't apply my rating. Stars not react on clicking. That is why submit operation ends with error 'In order to submit a review, you must also submit a rating.'. Look like something inside Parts.Stars.NoAverage.cshtml does not work. Please, help me.
According to the project's site it is a known issue: broken from version 1.7.2.
When looking at the code of the Parts_Reviews.cshtml it says the following on lines 20-24:
string showReviewUri = "#";
if (!Request.IsAuthenticated)
{
showReviewUri = Url.Action("LogOn", "Account", new { area = "Orchard.Users", ReturnUrl = Context.Request.RawUrl });
}
and on line 29:
<div id="createReviewLinkDiv"><span id="createReviewLinkSpan">#noReviewsYetText<a id="showReviewFormLink" href="#showReviewUri">#reviewLinkText</a></span></div>
Therefore, it was intended to let the anchor be # when the request is authenticated (you are logged on). This means it probably will be handled in JavaScript, which can be seen on lines 105-112:
$('#showReviewFormLink').click(function () {
$('#createReviewLinkDiv').slideToggle('fast', function () { $('#reviewFormDiv').slideToggle('fast'); });
return false;
});
$('#cancelCreateReviewLink').click(function () {
$('#reviewFormDiv').slideToggle('fast', function() { $('#createReviewLinkDiv').slideToggle('fast'); });
return false;
});
This piece of code should let you see the form to write a review, so something is going wrong there presumably. When there's something wrong in this jQuery code it probably gives an error in the console, so check out the browser's console when you click the 'Be the first to write a review' link.
This should get you further, if you don't know what to do please provide the error and I will try to dig more. I haven't downloaded the module so I don't have live feed.
Console of Firefox tells: $(...).live is not a function. It refers to Contrib.Stars.js source code file. This function is not supported in jquery now and i replaced it by .on() function in all places api.jquery.com/on. Now module works fine.
Check out my comment at the site below to see how I was was able to get it working again on Orchard 1.8.1:
Orchard Reviews Project Site
You basically just need to change 3 different lines in the Contrib.Stars.js file but I would recommend copying the .js file along with the Review module's different views to a custom theme directory, in order to override everything and force the Reviews module to use your edited .js file:
On line 12 & 13:
Change this:
$(".stars-clear").live(
"click",
To this:
$("body").on(
"click", ".stars-clear",
On line 44 & 45:
Change this:
.live(
"mouseenter",
To this:
.mouseenter(
On line 48 & 49:
Change this:
.live(
"mouseleave",
To this:
.mouseleave(

Resources