I'm using code like {{user.city}} in one of my templates to pull data from my MySQL DB on my Ionic Hybrid App.
On my frontend, For a value like
Bogotá
I get
Bogotá
I made sure to set my DB collation to utf8_unicode_ci.
I tried searching and it seems I need to decode the entities, but I haven't been able to do so.I tried using:
<div ng-bind-html={{user.city}}></div>
Please bare in mind I have several values like this, not only one.
I'm also using an Ionic App I got from a code market, and I'm not very ionic-savvy, so if you could provide detail I would be very grateful!
Thanks!
David
You must encode the locations in php.
Then in your controller:
$http.get("path").success(function(response) {
var res =response.results;
var locationtext =[];
for (key in res){
//in this case your locationsTEXT is the locations from the API
locationtext.push(JSON.parse(res[key].locationsTEXT));
}
$scope.city = locationtext;
}
In view:
<ion-list>
<ion-item ng-repeat="point in city track by $index">
<p>{{point}}</p>
</ion-item>
</ion-list>
Keep in mind that, above code is a general solution. You must adapt this code according to your variables and needs.
Hope this helps!
Related
I'm still a novice web developer, so please bear with me if I miss something fundamental !
I'm creating a backoffice for a Strapi backend, using react-admin.
React-admin library uses a 'data provider' to link itself with an API. Luckily someone already wrote a data provider for Strapi. I had no problem with step 1 and 2 of this README, and I can authenticate to Strapi within my React app.
I now want to fetch and display my Strapi data, starting with Users. In order to do that, quoting Step 3 of this readme : 'In controllers I need to set the Content-Range header with the total number of results to build the pagination'.
So far I tried to do this in my User controller, with no success.
What I try to achieve:
First, I'd like it to simply work with the ctx.set('Content-Range', ...) hard-coded in the controller like aforementioned Step 3.
Second, I've thought it would be very dirty to c/p this logic in every controller (not to mention in any future controllers), instead of having some callback function dynamically appending the Content-Range header to any fetchAll request. Ultimately that's what I aim for, because with ~40 Strapi objects to administrate already and plenty more to come, it has to scale.
Technical infos
node -v: 11.13.0
npm -v: 6.7.0
strapi version: 3.0.0-alpha.25.2
uname -r output: Linux 4.14.106-97.85.amzn2.x86_64
DB: mySQL v2.16
So far I've tried accessing the count() method of User model like aforementioned step3, but my controller doesn't look like the example as I'm working with users-permissions plugin.
This is the action I've tried to edit (located in project/plugins/users-permissions/controllers/User.js)
find: async (ctx) => {
let data = await strapi.plugins['users-permissions'].services.user.fetchAll(ctx.query);
data.reduce((acc, user) => {
acc.push(_.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']));
return acc;
}, []);
// Send 200 `ok`
ctx.send(data);
},
From what I've gathered on Strapi documentation (here and also here), context is a sort of wrapper object. I only worked with Express-generated APIs before, so I understood this snippet as 'use fetchAll method of the User model object, with ctx.query as an argument', but I had no luck logging this ctx.query. And as I can't log stuff, I'm kinda blocked.
In my exploration, I naively tried to log the full ctx object and work from there:
// Send 200 `ok`
ctx.send(data);
strapi.log.info(ctx.query, ' were query');
strapi.log.info(ctx.request, 'were request');
strapi.log.info(ctx.response, 'were response');
strapi.log.info(ctx.res, 'were res');
strapi.log.info(ctx.req, 'were req');
strapi.log.info(ctx, 'is full context')
},
Unfortunately, I fear I miss something obvious, as it gives me no input at all. Making a fetchAll request from my React app with these console.logs print this in my terminal:
[2019-09-19T12:43:03.409Z] info were query
[2019-09-19T12:43:03.410Z] info were request
[2019-09-19T12:43:03.418Z] info were response
[2019-09-19T12:43:03.419Z] info were res
[2019-09-19T12:43:03.419Z] info were req
[2019-09-19T12:43:03.419Z] info is full context
[2019-09-19T12:43:03.435Z] debug GET /users?_sort=id:DESC&_start=0&_limit=10& (74 ms)
While in my frontend I get the good ol' The Content-Range header is missing in the HTTP Response message I'm trying to solve.
After writing this wall of text I realize the logging issue is separated from my original problem, but if I was able to at least log ctx properly, maybe I'd be able to find the solution myself.
Trying to summarize:
Actual problem is, how do I set my Content-Range properly in my strapi controller ? (partially answered cf. edit 3)
Collateral problem n°1: Can't even log ctx object (cf. edit 2)
Collateral problem n°2: Once I figure out the actual problem, is it feasible to address it dynamically (basically some callback function for index/fetchAll routes, in which the model is a variable, on which I'd call the appropriate count() method, and finally append the result to my response header)? I'm not asking for the code here, just if you think it's feasible and/or know a more elegant way.
Thank you for reading through and excuse me if it was confuse; I wasn't sure which infos would be relevant, so I thought the more the better.
/edit1: forgot to mention, in my controller I also tried to log strapi.plugins['users-permissions'].services.user object to see if it actually has a count() method but got no luck with that either. Also tried the original snippet (Step 3 of aforementioned README), but failed as expected as afaik I don't see the User model being imported anywhere (the only import in User.js being lodash)
/edit2: About the logs, my bad, I just misunderstood the documentation. I now do:
ctx.send(data);
strapi.log.info('ctx should be : ', {ctx});
strapi.log.info('ctx.req = ', {...ctx.req});
strapi.log.info('ctx.res = ', {...ctx.res});
strapi.log.info('ctx.request = ', {...ctx.request});
ctrapi.log.info('ctx.response = ', {...ctx.response});
Ctx logs this way; also it seems that it needs the spread operator to display nested objects ({ctx.req} crash the server, {...ctx.req} is okay). Cool, because it narrows the question to what's interesting.
/edit3: As expected, having logs helps big time. I've managed to display my users (although in the dirty way). Couldn't find any count() method, but watching the data object that is passed to ctx.send(), it's equivalent to your typical 'res.data' i.e a pure JSON with my user list. So a simple .length did the trick:
let data = await strapi.plugins['users-permissions'].services.user.fetchAll(ctx.query);
data.reduce((acc, user) => {
acc.push(_.omit(user.toJSON ? user.toJSON() : user, ['password', 'resetPasswordToken']));
return acc;
}, []);
ctx.set('Content-Range', data.length) // <-- it did the trick
// Send 200 `ok`
ctx.send(data);
Now starting to work on the hard part: the dynamic callback function that will do that for any index/fetchAll call. Will update once I figure it out
I'm using React Admin and Strapi together and installed ra-strapi-provider.
A little boring to paste Content-Range header into all of my controllers, so I searched for a better solution. Then I've found middleware concept and created one that fits my needs. It's probably not the best solution, but do its job well:
const _ = require("lodash");
module.exports = strapi => {
return {
// can also be async
initialize() {
strapi.app.use(async (ctx, next) => {
await next();
if (_.isArray(ctx.response.body))
ctx.set("Content-Range", ctx.response.body.length);
});
}
};
};
I hope it helps
For people still landing on this page:
Strapi has been updated from #alpha to #beta. Care, as some of the code in my OP is no longer valid; also some of their documentation is not up to date.
I failed to find a "clever" way to solve this problem; in the end I copy/pasted the ctx.set('Content-Range', data.length) bit in all relevant controllers and it just worked.
If somebody comes with a clever solution for that problem I'll happily accept his answer. With the current Strapi version I don't think it's doable with policies or lifecycle callbacks.
The "quick & easy fix" is still to customize each relevant Strapi controller.
With strapi#beta you don't have direct access to controller's code: you'll first need to "rewrite" one with the help of this doc. Then add the ctx.set('Content-Range', data.length) bit. Test it properly with RA, so for the other controllers, you'll just have to create the folder, name the file, copy/paste your code + "Search & Replace" on model name.
The "longer & cleaner fix" would be to dive into the react-admin source code and refactorize so the lack of "Content-Range" header doesn't break pagination.
You'll now have to maintain your own react-admin fork, so make sure you're already committed into this library and have A LOT of tables to manage through it (so much that customizing every Strapi controller will be too tedious).
Before forking RA, please remember all the stuff you can do with the Strapi backoffice alone (including embedding your custom React app into it) and ensure it will be worth the trouble.
I'm in the progress of internationalizing an node expressjs-app. The idea is to serve the whole website within a language-subfolder (www.domain.com/en/). I'm kinda struggling wrapping my head around how to organize the app and it seems kinda hard to find some useful resources on this issue on stacko or the web in general.
I feel what makes it challenging here, is the fact that you have to achieve the best in three areas: Usability, SEO, and Performance.
There are a couple of questions:
Where are the response/selected languages to be stored? In the Session?
What is ideally the single source of throuth of the current language setting?
How is the language path affecting the language? (Changing the language to current path? Redirect to active/stored language?)
How are the routes to be organized? What Middleware strategies make sense for detecting and changing languages? Is it necessary to add to all internal links the language subpath, or can this be done by clever routing?
I would love to get some hints, resources, blog articles, repos where I can learn about best practices on this topic.
Cheers
I can highly recommend i18n for node.js. I have used it in 2 node Projects so far and it always worked like a charm. I then always had one json-File for each language I wanted to serve. You need to implement it once in your templates and then it hsould just work.
Regarding configuration an easy example:
// Configuration
app.configure(function() {
[...]
// default: using 'accept-language' header to guess language settings
app.use(i18n.init);
[...]
});
So than i18n will guess the language based on the users browser agent.
What I am always doing is not using an extra route rather I am using a parameter lang and than it is possible to change the language all the time per hrefs. E.g. https://example.com/?lang=de will change to german language. Of course you need in every get of express a helper function to detect if another language is set and then you can handle that. For me that was the easiest way but regarding SEO that is not the best I think.
Of course you can also handle it e.g. with different domains/subdomains as airbnb is doing that. https://nl.airbnb.com vs. https://www.airbnb.com vs. https://www.airbnb.de or as you mentioned with routes does also work very well. But I think that is related with a little more work.
For pros and cons regarding SEO and other you can just google a little bit and have a look at this quora question which also highly recommends the Google Best Practices at this topic.
You don't even need to use a library for a simple localization. I'll show you a simple example:
Let's say you have your language strings in a json at global scope (can be in a file or db too) :
var languageData = {
'en': {
'LOGIN_BTN': 'Login now',
'REGISTER_BTN': 'Register'
},
'tr': {
'LOGIN_BTN': 'Giris',
'REGISTER_BTN': 'Kayit'
}
}
Let's create simple middleware:
function getLanguageStrings(req, res, next) {
var lang = req.acceptsLanguages('en', 'tr', 'fr')
var selectedLang = lang ? lang : 'en' // default to english
req.languageStrings = languageData[selectedLang]
next()
}
Above, I used acceptsLanguages() method to get the preferred language of browser, but you can set cookie from client side and read it in our middleware if you want to.
With the req.languageStrings = languageData[selectedLang] line, I've attached strings to current request so that next middleware can use it.
Let's use our middleware:
app.use(getLanguageStrings)
And in the route, render them to view:
app.get("/info", function (req, res) {
res.render("info.html", {
languageStrings: req.languageStrings
})
})
In view, you now use it with your preferred template engine:
<button class="btn">{{languageStrings.LOGIN_BTN}}</button>
<button class="btn">{{languageStrings.REGISTER_BTN}}</button>
For this purpose I used i18n module (pretty much the same procedure with other localization modules). You keep your translations in simple json files and by default i18n checks for a language depending on a cookie sent by client.
That is pretty much it, I think there is a few other ways to get the language instead of using cookies, for example by request params (as you've mentioned) or by value sent within request body.
It really depends on your needs. This is only available if you use i18n-node-2 module, for the first one you have to use cookies (correct me if I'm wrong).
Example I've created to show how to set it up on your server side.
Localization with Express and i18n
Update:
For i18n-node-2
Like the README.md file says, there is a few functions which you can choose to detect / set needed language:
setLocale(locale)
setLocaleFromQuery([request])
setLocaleFromCookie([request])
setLocaleFromSessionVar([request])
setLocaleFromEnvironmentVariable()
Documentation: i18n-node-2
I'm working with polymer and I'm testing all in my local environment and all is working as expected, but the problem is when I'll move it into production, where the url shouldn't be the same as in my local. In node.js I can set it via the config.json file and catch it with app.get('config') or something like that, but in polymer I cant get it, and the only chance I have is to create another endpoint with all the config.json config file, but, and here is the best part, I should hardcode this url in polymer!! (so annoying). There is some way or library or component or something that I'm missing ?
Thanks in advance guys! StackOverflow and the users helped my a lot!
UPDATE
The thing is that I'm using custom elements (because I had handlebars too and there is a little problem with the {{}}) so, in the custom elements that I created I have this (for example in the core-ajax call):
<core-ajax id="login" auto="false" method="POST" contentType="application/json" url="/app/middle/login" ....></core-ajax>
as you can see the url is a little bit hardcoded, I want to use something like this (this is what I have on the node.js endpoint application):
router.post(endpoint.login, function (req, res) {
and I want to got something like that over polymer
<core-ajax id="login" auto="false" method="POST" contentType="application/json" url="{{login}}" ... ></core-ajax>
<script>
Polymer('log-form', {
login: endpoint.login,
ready: function () {
....
})
});
</script>
I'ts more clear now? I'm not so strong on english language.
Thanks Guys, for all!
I'm trying out node and some frameworks for node atm, specifically locomotive. However, i seem to be stuck on routing using locomotive. A couple questions i can't find the answer to, so here goes:
why does the locomotive out-of-box install use index.html.ejs as a
filename? Why not just index.ejs? What's the benefit?
i'm trying to add a route to a view: searchName.html.ejs which i
added in the views folder. To achieve this i made a toolController
like this:
var locomotive = require('locomotive').Controller,
toolController = new Controller();
toolController.searchName = function() {
this.render();
}
module.exports = toolController;
I also added a route in routes.js like so:
this.match('searchName', 'tool#searchName');
However, that doesn't work (and yet it's what the documentation says ought to work). The result is a 404 error. So how do i make that route work?
Suppose i want to make a route to eg, anExample.html? How do i go
about that? I notice that in the out-of-the-box app from
locomotive, you cannot enter localhost:3000/index.html . Nor even
localhost:3000/index This seems highly impractical to me, as there
are plenty of users who'll add the specific page they want to go to.
So how can i make that work?
PS: I went through all questions regarding this on stackoverflow and searched the web, but i still can't figure this out.enter code here
The benefit is that this naming scheme allows you to specify several different formats for a single route. So you could have search_name.html.ejs and search_name.xml.ejs, then respond with either view depending on what your client is expecting.
There are a couple issues with the example code you posted. You should be seeing a more descriptive error than a 404, so I'm not sure what's happening there, but here are the fixes to your code that work in my environment.
In the controller:
//tool_controller.js
var locomotive = require('locomotive');
var toolController = new locomotive.Controller();
toolController.searchName = function() {
this.render();
};
module.exports = toolController;
In routes.js:
//routes.js
module.exports = function routes()
{
this.match('searchName', 'tool#searchName');
}
Then, you'll need to change the view to this: views/tool/search_name.html.ejs. It's not clear from the documentation, but locomotive automatically lowercases and underscores actions that are camel-cased, like searchName.
Now start the app and browse to http://localhost:3000/searchName
If you just want to serve a static html file, the easiest way is to just drop it in the public folder. This folder is specifically for serving up static content like client-side js, css, etc. And it works just fine for serving static HTML as well.
The Spotify UI guidelines for Spotify apps (at https://developer.spotify.com/technologies/apps/guidelines/design/) say "When listing tracks in your app, use our standardized track listings". I cannot find any examples in the documentation on how to use these "standardized track listings". By using the Inspector I have found classes in list.css (such as sp-list and sp-item) which it looks like I need to use but have not been able to work out quite how to use these to recreate the look of the Spotify track listings.
The Billboard Top Charts app appears to use track listings like I need, but I can't find ay way to see how they are doing this as the Inspector only works for your own apps as far as I can tell.
Does anybody have any advice or examples?
Some examples
sp = getSpotifyApi(1);
var m = sp.require("sp://import/scripts/api/models");
var v = sp.require("sp://import/scripts/api/views");
// Example 1
var tpl = new m.Playlist();
var tempList = new v.List(tpl);
tpl.add(m.Track.fromURI("spotify:track:4z4t4zEn4ElVPGmDWCzRQf"));
tpl.add(m.Track.fromURI("http://open.spotify.com/track/7E8JGVhbwWgAQ1DtfatQEl"));
tpl.add(m.Track.fromURI("spotify:track:40YBc3mR3yyqyYvtesQOMj"));
tpl.add(m.Track.fromURI("spotify:local:Rolling+Stones:A+Bigger+Bang:Rain+Fall+Down:293"));
document.body.appendChild(tempList.node);
// Example 2
var pl = m.Playlist.fromURI("spotify:user:username:playlist:424km2k4m24");
var list = new v.List(pl);
document.body.appendChild(list.node);
// Example 3
var album = m.Album.fromURI("spotify:album:1vWnB0hYmluskQuzxwo25a");
var albumList = new v.List(album);
albumList.node.classList.add("album");
document.body.appendChild(albumList.node);
Thanks for asking, I had exactly the same question too!
I too get the issue where i get no actual content added - just the wrapper div. Not including api.css makes it work, but the list is obviously not styled. Including css/list.css directly breaks it too. Creating my own copy and selectively commenting out list.css I found the offending rule to be:
.sp-list > div {
if you change this to be
.sp-list {
then it renders fine. No idea what is going on. Obviously this solution is not idea because I've just duplicated what's meant to be a common resource...