分享一个前端的 urlFor 函数,实现 Flask 后端 url_for 的类似功能

2015-07-16 18:17:06 +08:00
 hustlzp

本来在 HTML 中可以直接调用后端的 url_for 的,比如:

<script>
    var url = "{{ url_for('site.index') }}";
</script>

不过如果是外部引用的 js 的话,就没办法(除非以 HTML data 属性作为中介进行传递,但略麻烦),所以就写了个前端的 urlFor,和后端的 url_for 对应。

首先在后端加入这一段代码:

rules = {}
for endpoint, _rules in iteritems(app.url_map._rules_by_endpoint):
    if any(item in endpoint for item in ['_debug_toolbar', 'debugtoolbar', 'static']):
        continue
    rules[endpoint] = [{'rule': rule.rule} for rule in _rules]

app.jinja_env.globals.update({
    'rules': rules,
})

然后在前端加入以下代码:

// Find out params in routing rules
var pattern = new RegExp("<[^:]*:?([^>]+)>", "g");
var result = null;

$.each(g.rules, function (endpoint, rules) {
    $.each(rules, function (index, rule) {
        rule.params = [];
        while ((result = pattern.exec(rule.rule)) !== null) {
            rule.params.push(result[1]);
        }
    });
});

/**
 * Check whether str ends with suffix.
 * @param str
 * @param suffix
 * @returns {boolean}
 */
function endsWith(str, suffix) {
    return str.indexOf(suffix, str.length - suffix.length) !== -1;
}

/**
 * Generate url for the endpoint.
 * urlFor(endpoint [, parameters])
 * @param endpoint
 * @param parameters
 * @returns url for the endpoint.
 */
function urlFor(endpoint, parameters) {
    var url = null,
        params = [],
        maxMatchDegree = 0.0,
        keys;

    parameters = ($.type(parameters) !== 'undefined') ? parameters : {};

    if (g.rules[endpoint] === undefined) {
        throw new Error("Uncorrect endpoint in " + "urlFor(\"" + endpoint + "\", " +
            JSON.stringify(parameters) + ")");
    }

    keys = $.map(parameters, function (value, key) {
        return key;
    });

    // Find the first matched rule among rules in this endpoint.
    $.each(g.rules[endpoint], function (index, rule) {
        var match = true,
            currentMatchDegree = 0.0;

        $.each(rule.params, function (index, param) {
            if ($.inArray(param, keys) === -1) {
                match = false;
                return false;
            }
        });

        if (match) {
            currentMatchDegree = parseFloat(rule.params.length) / keys.length;
            if (currentMatchDegree > maxMatchDegree || url === null) {
                maxMatchDegree = currentMatchDegree;
                url = rule.rule;
                params = rule.params;
            }
        }
    });

    if (url) {
        $.each(keys, function (index, key) {
            // Build in params
            if ($.inArray(key, params) > -1) {
                url = url.replace(new RegExp("<[^:]*:?" + key + ">"), parameters[key]);
            } else {
                // Query string params
                if (url.indexOf("?") === -1) {
                    url += "?";
                }
                if (!endsWith(url, '?')) {
                    url += "&";
                }
                url += key + "=" + parameters[key];
            }
        });
    } else {
        throw new Error("Uncorrect parameters in urlFor(\"" + endpoint + "\", " +
            JSON.stringify(parameters) + ")");
    }

    return url;
}

使用方法:

url_for('site.index')    '/'
url_for('user.view', {uid: 1})    '/user/1'
url_for('user.view', {uid: 1, other:'other'})    '/user/1?other=other'
url_for('user.view', {other: 'other'})    Uncorrect parameters in urlFor("user.view", {other:'other'})
5419 次点击
所在节点    Python
3 条回复
hustlzp
2015-07-16 18:24:51 +08:00
漏了一个地方,需要提将 rules 赋予 JS 全局变量 g 中:

<script>
g.rules = {{ rules|safe }};
</script>
dagger
2015-07-16 21:26:35 +08:00
重度flask瘾患者啊~
话说这是直接把py翻译到js的吗?
hustlzp
2015-07-18 23:54:34 +08:00
@dagger 恩,差不多

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/206185

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX